PHPUnit模拟对象,与SUT的类型相同,是SUT的属性

时间:2016-06-28 22:23:38

标签: php unit-testing mocking phpunit

我有一个名为mysqli_store_result的类,它有一个名为TestClass的属性。 parent属性与parent的类型相同。 下面显示的是我要测试的TestClass的一部分。

TestClass

方法class TestClass { /** * @param array $parentIds * @return ApiLock[] */ protected function getParentLocks(array $parentIds) { if ($this->getParent()) { $id = array_pop($parentIds); return $this->getParent()->getLocks($id, $parentIds); } return []; } /** * @param array $ids * @return ApiLock[] */ protected function getLocks($id, array $ids) { $locks = $this->getParentLocks($ids); if ($this->config->getLockName()) { $locks[] = new ApiLock($this->config->getLockName() . '.' . $id, $this->config->getLockWait()); } return $locks; } } 调用getLocks方法。为了测试这个类,我模拟了getParentLocks对象并设置为SUT的父级。类似的东西。

TestClass

但是当我运行测试时,$apiLock = $this->getMockBuilder(ApiLock::class) ->disableOriginalConstructor() ->getMock(); $parent = $this->getMockBuilder(TestClass::class) ->disableOriginalConstructor() ->getMock(); $parent->expects($this->any()) ->method('getLocks') ->will($this->returnValue([$apiLock])); $testClass->setParent($parent); 对象上的方法setLocks将不会返回存根值,但实际上会调用父对象上的parent方法并且测试失败。可能是我没有看到明显的东西。 应该调用存根方法而不是父对象上的实际方法。 请提前帮助我。

1 个答案:

答案 0 :(得分:1)

问题与父子关系无关。

您应该定义要模拟的方法:

    $parent = $this->getMockBuilder(TestClass::class)
        ->setMethods(['setLocks', 'getLocks'])
        ->disableOriginalConstructor()
        ->getMock();

将定期调用未模拟的方法,并忽略您的期望。您可能正在使用PHPUnit 4,因为在PHPUnit 5中,您会在警告中看到您为未被模拟的方法定义了期望。

更新:如果您不打电话给setMethods(),那么所有方法都应该被嘲笑,但这仅适用于公开非抽象方法

这是PHPUnit_Framework_MockObject_Generator中自动确定要模拟的方法的代码:

public function getClassMethods($className)
{
    $class   = new ReflectionClass($className);
    $methods = [];
    foreach ($class->getMethods() as $method) {
        if ($method->isPublic() || $method->isAbstract()) {
            $methods[] = $method->getName();
        }
    }
    return $methods;
}

因此,您可以模拟受保护的方法(以及不会触发__call()的不存在的方法),但除了公共方法之外,您需要明确指定它们。