35

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

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

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");
    }
}
4

5 回答 5

55

通过阅读链接页面,SimpleTest 部分模拟似乎是一个模拟,其中只有一些方法被覆盖。如果这是正确的,则该功能由普通的 PHPUnit 模拟处理。

在 aPHPUnit_Framework_TestCase中,您创建一个模拟

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

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

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

将创建一个删除和函数的模拟实例Class_To_Mock,准备好指定它们的返回值。insertupdate

此信息在phpunit 文档中。

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

于 2009-07-27T05:48:25.693 回答
23

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

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

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

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

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

于 2017-03-29T03:50:03.190 回答
3

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

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

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

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

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

在不跳入接口的情况下,这就是我要做的:

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();
    }
}

现在很容易模拟:

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");
    }
}
于 2011-03-10T18:20:13.623 回答
1

该方法setMethods已被弃用。现在它起作用了:

$mock = $this->getMockBuilder(ClassToMock::class)
    ->onlyMethods(['insert', 'update'])
    ->getMock();
于 2021-07-22T19:14:44.770 回答
-1

可能是您正在模拟的课程不在范围内(我遇到了这个问题)。解决后,我能够模拟一个函数并测试另一个函数的实际逻辑。

$mockController = $this->getMockBuilder('ControllerClassName')
                       ->setMethods(['functionToMock'])
                       ->getMock();

$result = $mockController->anotherFunction();
$this->assertEquals(true, $result);
于 2021-04-21T14:54:37.500 回答