与 SUT 类型相同且是 SUT 的 属性 的 PHPUnit 模拟对象
PHPUnit mocking object that is the same type as the SUT and is a property of the SUT
我有一个名为 TestClass
的 class,它有一个名为 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
方法。为了测试这个 class,我模拟了 TestClass
对象并将其设置为 SUT 的父对象。类似于此。
$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);
但是当我 运行 测试时,parent
对象上的方法 setLocks
不会 return 存根值但实际上会调用 setLocks
父对象上的方法,测试失败。可能是我没有看到明显的东西。
应该调用存根方法而不是父对象上的实际方法。
请帮助我提前谢谢。
问题与父子关系无关
您应该定义要模拟的方法:
$parent = $this->getMockBuilder(TestClass::class)
->setMethods(['setLocks', 'getLocks'])
->disableOriginalConstructor()
->getMock();
未被模拟的方法将被定期调用,而您的期望将被忽略。您可能正在使用 PHPUnit 4,因为在 PHPUnit 5 中,您会在警告中看到您定义了对未模拟的方法的期望。
更新: 的确,如果不调用setMethods()
,所有方法都应该被mock,但这只适用于public 非抽象方法.
这是 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()
的不存在的方法),但除了 public 方法之外,您需要明确指定它们。
我有一个名为 TestClass
的 class,它有一个名为 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
方法。为了测试这个 class,我模拟了 TestClass
对象并将其设置为 SUT 的父对象。类似于此。
$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);
但是当我 运行 测试时,parent
对象上的方法 setLocks
不会 return 存根值但实际上会调用 setLocks
父对象上的方法,测试失败。可能是我没有看到明显的东西。
应该调用存根方法而不是父对象上的实际方法。
请帮助我提前谢谢。
问题与父子关系无关
您应该定义要模拟的方法:
$parent = $this->getMockBuilder(TestClass::class)
->setMethods(['setLocks', 'getLocks'])
->disableOriginalConstructor()
->getMock();
未被模拟的方法将被定期调用,而您的期望将被忽略。您可能正在使用 PHPUnit 4,因为在 PHPUnit 5 中,您会在警告中看到您定义了对未模拟的方法的期望。
更新: 的确,如果不调用setMethods()
,所有方法都应该被mock,但这只适用于public 非抽象方法.
这是 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()
的不存在的方法),但除了 public 方法之外,您需要明确指定它们。