php 相当于 PHPUnit 中的 SimpleTest“部分模拟”?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1164192/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 01:18:33  来源:igfitidea点击:

Equivalent of SimpleTest "partial mocks" in PHPUnit?

phpphpunitsimpletest

提问by Shabbyrobe

I'm trying to migrate a bunch of tests from SimpleTest to PHPUnit and I was wondering if there is an equivalent for SimpleTest's partial mocks.

我正在尝试将一堆测试从 SimpleTest 迁移到 PHPUnit,我想知道 SimpleTest 的部分模拟是否有等价物。

Update: I can't seem to find anything in the docs which suggests that this feature is available, but it occurred to me that I could just use a subclass. Is this a good or bad idea?

更新:我似乎无法在文档中找到任何表明此功能可用的内容,但我突然想到我可以只使用子类。这是个好主意还是坏主意?

class StuffDoer {
    protected function doesLongRunningThing() {
        sleep(10);
        return "stuff";
    }
    public function doStuff() {
        return $this->doesLongRunningThing();
    }
}
class StuffDoerTest {
    protected function doesLongRunningThing() {
        return "test stuff";
    }
}
class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
    public function testStuffDoer() {
        $sd = new StuffDoerTest();
        $result = $sd->doStuff();
        $this->assertEquals($result, "test stuff");
    }
}

回答by Brenton Alker

From reading the linked page, a SimpleTest partial mock seems to be a mock where only some of the methods are overridden. If this is correct, that functionality is handled by a normal PHPUnit mock.

从阅读链接的页面来看,SimpleTest 部分模拟似乎是一个仅覆盖某些方法的模拟。如果这是正确的,则该功能由普通 PHPUnit 模拟处理。

Inside a PHPUnit_Framework_TestCase, you create a mock with

在 a 中PHPUnit_Framework_TestCase,您创建一个模拟

$mock = $this->getMock('Class_To_Mock');

Which creates an mock instance where all methods do nothing and return null. If you want to only override some of the methods, the second parameter to getMockis an array of methods to override.

它创建了一个模拟实例,其中所有方法都不执行任何操作并返回 null。如果您只想覆盖某些方法,则第二个参数getMock是要覆盖的方法数组。

$mock = $this->getMock('Class_To_Mock', array('insert', 'update'));

will create an mock instance of Class_To_Mockwith the insertand updatefunctions removed, ready for their return values to be specified.

将创建一个模拟实例Class_To_Mockinsertupdate功能去掉,准备为他们的返回值进行指定。

This information is in the phpunit docs.

此信息位于phpunit 文档中

Note, this answershows more up to date code examples, for PHPUnit versions starting 5.4

请注意此答案显示了更多最新的代码示例,适用于 PHPUnit 5.4 版本开始

回答by Nobu

PHPUnit_Framework_TestCase::getMockis deprecated since phpunit 5.4. We can use setMethodsinstead.

PHPUnit_Framework_TestCase::getMock自 phpunit 5.4 起已弃用。我们可以用setMethods

setMethods(array $methods) can be called on the Mock Builder object to specify the methods that are to be replaced with a configurable test double. The behavior of the other methods is not changed. If you call setMethods(null), then no methods will be replaced.

可以在 Mock Builder 对象上调用 setMethods(array $methods) 以指定要替换为可配置测试替身的方法。其他方法的行为没有改变。如果您调用 setMethods(null),则不会替换任何方法。

https://phpunit.de/manual/current/en/test-doubles.html

https://phpunit.de/manual/current/en/test-doubles.html

$observer = $this->getMockBuilder(Observer::class)
                 ->setMethods(['update'])
                 ->getMock();

Note that the above getMockis PHPUnit_Framework_MockObject_MockBuilder::getMock. (phpunit5.6)

注意上面getMockPHPUnit_Framework_MockObject_MockBuilder::getMock. (phpunit5.6)

回答by Ian

I don't think PHPUnit supports partial mocks for the system under test. If you're trying to isolate methods then I'm sure your implementation works - I've done that too.

我认为 PHPUnit 不支持被测系统的部分模拟。如果您正在尝试隔离方法,那么我确定您的实现有效 - 我也这样做了。

However, I try to avoid doing this, for a couple of reasons.

但是,出于几个原因,我尽量避免这样做。

First, it couples your test very tightly to the internal implementation of the class. Do you really care whether a method called doesLongRunningThingwas called, or is it more important that the "LongRunningThing" got done?

首先,它将您的测试与类的内部实现非常紧密地结合在一起。你真的关心是否调用了一个doesLongRunningThing被调用的方法,还是“LongRunningThing”完成更重要?

Second, when I run into this it always makes me wonder whether I've got one class doing the job of two. An extract classrefactoring might be in order. The testing becomes much easier if doesLongRunningThing()becomes its own class, even with a single method.

其次,当我遇到这种情况时,我总是想知道是否有一个班级在做两个班级的工作。一个提取类重构可能是为了。如果doesLongRunningThing()成为它自己的类,即使使用单个方法,测试也会变得容易得多。

I believe the solution is to inject the services your SUT depends on (http://en.wikipedia.org/wiki/Dependency_injection). This also makes the DoesLongRunningThingimplementation more testable.

我相信解决方案是注入您的 SUT 所依赖的服务 (http://en.wikipedia.org/wiki/Dependency_injection)。这也使DoesLongRunningThing实现更具可测试性。

Without jumping into interfaces, here's what I would do:

无需跳入界面,这就是我要做的:

class DoesLongRunningThing {
    public function execute() {
        sleep(10);
        return "stuff";
    }
}

class StuffDoer {
    protected $doesLongRunningThing;

    public function setLongRunningThinger(DoesLongRunningThing $obj) {
        $this->doesLongRunningThing = $obj;
    }

    public function doStuff() {
        return $this->doesLongRunningThing->execute();
    }
}

Now it's easy to mock:

现在很容易模拟:

class StuffDoerTestCase extends PHPUnit_Framework_TestCase {
    public function testStuffDoer() {
        $dlrtMock = $this->getMock('DoesLongRunningThing');
        $dlrtMock->expects($this->any())->will($this->returnValue("test stuff"));

        $sd = new StuffDoer();
        $sd->setLongRunningThinger($dlrtMock);
        $result = $sd->doStuff();
        $this->assertEquals($result, "test stuff");
    }
}