PHPUnit中的SimpleTest“部分模拟”的等价物?

时间:2009-07-22 09:50:18

标签: php phpunit simpletest

我正在尝试将一堆测试从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");
    }
}

5 个答案:

答案 0 :(得分:47)

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

PHPUnit_Framework_TestCase内,您可以使用

创建模拟
$mock = $this->getMock('Class_To_Mock');

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

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

将创建Class_To_Mock的模拟实例,删除insertupdate函数,为其指定的返回值做好准备。

此信息位于phpunit docs

注意this answer显示更多最新代码示例,适用于从5.4开始的PHPUnit版本

答案 1 :(得分:12)

PHPUnit_Framework_TestCase::getMock is deprecated since phpunit 5.4。我们可以使用setMethods代替。

  

可以在Mock Builder对象上调用setMethods(array $ methods)来指定要用可配置的测试double替换的方法。其他方法的行为不会改变。如果调用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)

答案 2 :(得分:2)

我认为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");
    }
}

答案 3 :(得分:0)

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

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

$result = $mockController->anotherFunction();
$this->assertEquals(true, $result);

答案 4 :(得分:0)

方法 setMethods 已弃用。现在它可以工作了:

$mock = $this->getMockBuilder(ClassToMock::class)
    ->onlyMethods(['insert', 'update'])
    ->getMock();