【问题标题】:PHPUnit mocking object that is the same type as the SUT and is a property of the SUTPHPUnit 模拟对象,与 SUT 类型相同,是 SUT 的属性
【发布时间】:2016-06-28 22:23:38
【问题描述】:

我有一个名为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 方法。为了测试这个类,我模拟了 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 不会返回存根值,但实际上会调用父对象上的setLocks 方法并且测试失败。可能是我没有看到明显的东西。 应该调用存根方法而不是父对象上的真实方法。 请帮助我提前谢谢。

【问题讨论】:

  • 不确定您使用的是什么模拟/测试框架。但我认为它应该有一个 when() 方法,所以 Mock 知道它应该为哪个方法调用返回什么存根数据。正如你所说,你的存根没有被称为你的 Mock 实际上是一个间谍。

标签: php unit-testing mocking phpunit


【解决方案1】:

问题与父子关系无关。

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

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

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


更新:确实,如果你不调用setMethods(),所有方法都应该被mock,但这仅适用于公共非抽象方法

这是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() 的不存在的方法),但除了公共方法之外,您需要明确指定它们。

【讨论】:

  • 谢谢。解决了它。但是,我认为如果不使用 setMethods,则可以模拟所有方法。
  • @ShikharSubedi 我认为,这仅适用于 public 方法
猜你喜欢
  • 2013-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-18
  • 2015-06-06
  • 2020-04-04
  • 2021-10-31
相关资源
最近更新 更多