【问题标题】:PHPUnit Stubbing Class methods declared as "final"声明为“final”的 PHPUnit 存根类方法
【发布时间】:2011-03-06 00:39:50
【问题描述】:

我正在为一个使用mock调用另一个类的方法的类方法编写单元测试,只有需要调用的方法被声明为final,所以PHPUnit无法mock它。我可以采取其他方法吗?

示例:

要模拟的类

class Class_To_Mock
{
    final public function needsToBeCalled($options)
    {
        ...
    }
}

我的测试用例

class MyTest extends PHPUnit_Framework_TestCase
{
    public function testDoSomething()
    {
        $mock = $this->getMock('Class_To_Mock', array('needsToBeCalled'));
        $mock->expects($this->once())
             ->method('needsToBeCalled')
             ->with($this->equalTo(array('option'));
    }
}

编辑:如果使用 Mike B 提供的解决方案,并且您有一个用于您正在模拟的对象的 setter/getter 进行类型检查(以确保将正确的对象传递到 setter),您需要模拟您正在测试的类的 getter 并让它返回另一个模拟。

示例:

要模拟的类

class Class_To_Mock
{
    final public function needsToBeCalled($options)
    {
        ...
    }
}

模拟

class Class_To_MockMock
{
    public function needsToBeCalled($options)
    {
        ...
    }
}

要测试的类

class Class_To_Be_Tested
{
    public function setClassToMock(Class_To_Mock $classToMock)
    {
        ...
    }

    public function getClassToMock()
    {
        ...
    }

    public function doSomething()
    {
        $this->getClassToMock()
             ->needsToBeCalled(array('option'));
    }
}

我的测试用例

class MyTest extends PHPUnit_Framework_TestCase
{
    public function testDoSomething()
    {
        $classToTest = $this->getMock('Class_To_Be_Tested', array('getClassToMock'));

        $mock = $this->getMock('Class_To_MockMock', array('needsToBeCalled'));

        $classToTest->expects($this->any())
                    ->method('getClassToMock')
                    ->will($this->returnValue($mock));

        $mock->expects($this->once())
             ->method('needsToBeCalled')
             ->with($this->equalTo(array('option'));

        $classToTest->doSomething();
    }
}

【问题讨论】:

    标签: php unit-testing mocking phpunit


    【解决方案1】:

    我认为 PHPUnit 不支持对 final 方法进行存根/模拟。您可能需要为这种情况创建自己的存根并做一些扩展技巧:

    class myTestClassMock {
      public function needsToBeCalled() {
        $foo = new Class_To_Mock();
        $result = $foo->needsToBeCalled();
        return array('option');
      }
    }
    

    Chapter 11. Test Doubles下的 PHPUnit 手册中找到这个

    限制

    请注意,final、private 和 static 方法不能被存根或模拟。它们被 PHPUnit 的测试双重功能忽略并保留其原始行为。

    【讨论】:

    • 如果您正在测试的类依赖于对被模拟的类/方法进行类型检查的 getter/setter,则需要一个额外的步骤。我将此添加到原始帖子中。
    • @rr 请考虑将其添加为答案,而不是将解决方案放在问题中。
    • @BartoszKP 理论是一件美好的事情。恕我直言,我对原始问题提供了直接答案。如果 OP 编辑​​了他的问题,我没有义务继续更新我的答案以匹配这个问题的移动目标。此外,此内容已存在 3 年以上,平均每天点击不到 1 次。所以最原始的问题和答案并不是那么有趣。不要模拟 final 方法。不要引入模拟 final 方法的变通方法,你的代码会更干净。
    • @MikeB 当然,您没有义务做任何事情。我只是建议清理整个线程。我不是 PHP 专家,所以我没有深入研究细节。如果您的答案仍然有效,则应将 OP 问题的解决方案部分移至另一个答案。
    • @MikeB 我同意Don't mock final methods.. don't introduce work-arounds for mocking final methods and your code will be much cleaner,问题是旧的遗留代码迫使你这样做:'(
    【解决方案2】:

    我今天偶然发现了这个问题。另一种选择是模拟类实现的接口,假设它实现了一个接口并且您使用该接口作为类型提示。

    例如,考虑到问题,您可以创建一个接口并按如下方式使用它:

    interface Interface_To_Mock
    {
        function needsToBeCalled($options);
    }
    
    class Class_To_Mock implements Interface_To_Mock
    {
        final public function needsToBeCalled($options)
        {
            ...
        }
    
    }
    
    class Class_To_Be_Tested
    {
        public function setClassToMock(Interface_To_Mock $classToMock)
        {
            ...
        }
    
        ...
    }
    
    class MyTest extends PHPUnit_Framework_TestCase
    {
        public function testDoSomething()
        {
            $mock = $this->getMock('Interface_To_Mock', array('needsToBeCalled'));
            ...
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-04
      • 1970-01-01
      • 2020-03-24
      • 1970-01-01
      • 2022-08-04
      • 2013-02-25
      • 1970-01-01
      相关资源
      最近更新 更多