douzhongjiu2263 2016-06-28 22:23
浏览 21
已采纳

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

I have a class called TestClass which has a property called parent. The parent property is the same type as the TestClass. Show below is a portion of the TestClass I am trying to test.

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

}

The method getLocks calls the getParentLocks method. To test this class, I have mocked TestClass object and set as the parent of the SUT. Something similar to this.

$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);

But when i run the tests , the method setLocks on the parent object will not return the stubbed value but will actually call the setLocks method on the parent object and the test fails. May be I am not seeing something obvious. The stubbed method should be called instead of the real method on the parent object. Please help me thanks in advance.

  • 写回答

1条回答 默认 最新

  • douzhangcuo2174 2016-06-29 07:33
    关注

    The problem doesn't have anything to do with the parent-child relation.

    You should define which methods you want to mock:

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

    Methods which are not mocked will be called regularly, and your expectations are ignored. You are probably using PHPUnit 4, because in PHPUnit 5 you would see in a warning that you defined an expectation for a method that is not mocked.


    Update: It's true that if you don't call setMethods(), all methods should be mocked, but this only applies to public non-abstract methods.

    This is the code in PHPUnit_Framework_MockObject_Generator that automatically determines methods to be mocked:

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

    So you can mock protected methods (and also nonexistent methods that would trigger __call()), but other than public methods, you need to specify them explicitly.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥50 potsgresql15备份问题
  • ¥15 Mac系统vs code使用phpstudy如何配置debug来调试php
  • ¥15 目前主流的音乐软件,像网易云音乐,QQ音乐他们的前端和后台部分是用的什么技术实现的?求解!
  • ¥60 pb数据库修改与连接
  • ¥15 spss统计中二分类变量和有序变量的相关性分析可以用kendall相关分析吗?
  • ¥15 拟通过pc下指令到安卓系统,如果追求响应速度,尽可能无延迟,是不是用安卓模拟器会优于实体的安卓手机?如果是,可以快多少毫秒?
  • ¥20 神经网络Sequential name=sequential, built=False
  • ¥16 Qphython 用xlrd读取excel报错
  • ¥15 单片机学习顺序问题!!
  • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上