【问题标题】:PHPUnit - multiple stubs of same classPHPUnit - 同一类的多个存根
【发布时间】:2010-03-22 22:32:37
【问题描述】:

我正在为 Foo 类构建单元测试,而且我对单元测试还很陌生。

我的类的一个关键组件是BarCollection 的一个实例,它包含许多Bar 对象。 Foo 中的一个方法遍历集合并在集合中的每个 Bar 对象上调用几个方法。我想使用存根对象为我的测试类生成一系列响应。如何让Bar 存根类在迭代时返回不同的值?我正在尝试按照以下方式做一些事情:

$stubs = array();
foreach ($array as $value) {
    $barStub = $this->getMock('Bar');
    $barStub->expects($this->any())
            ->method('GetValue')
            ->will($this->returnValue($value));
    $stubs[] = $barStub;
}
// populate stubs into `Foo`

// assert results from `Foo->someMethod()`

所以Foo->someMethod() 将根据从Bar 对象接收到的结果生成数据。但是,只要数组长于一个,这就会给我以下错误:

There was 1 failure:

1) testMyTest(FooTest) with data set #2 (array(0.5, 0.5))
Expectation failed for method name is equal to <string:GetValue> when invoked zero or more times.
Mocked method does not exist.
/usr/share/php/PHPUnit/Framework/MockObject/Mock.php(193) : eval()'d code:25

我的一个想法是使用-&gt;will($this-&gt;returnCallback()) 调用回调方法,但我不知道如何向回调指示哪个Bar 对象正在进行调用(以及因此给出什么响应)。

另一个想法是使用onConsecutiveCalls() 方法或类似方法,告诉我的存根第一次返回 1,第二次返回 2,依此类推,但我不确定如何执行此操作。我还担心,如果我的班级除了对集合进行有序迭代之外的任何其他操作,我将无法对其进行测试。

【问题讨论】:

    标签: php unit-testing collections phpunit stub


    【解决方案1】:

    很遗憾,我不确定您是否可以使用 getMock() 来解决您的实际问题,但我对 getMock() 本身的经验并不丰富。

    我只能临时想到,但不知道您的 Bar 类,这可能无济于事:getMock() 的第三个参数允许您传递构造函数参数(作为一个数组)。

    我将创建我自己的模拟类扩展 Bar 作为测试助手(“只是另一个恰好仅在测试中使用的类”的花哨名称),它完全符合我的喜好,并将它们中的一系列注入到你的Foo 对象。这为您提供了您想要的所有控制,因为您可以完全替换有问题的方法,而 getMock() 不会这样做。当然,这也意味着您没有在此测试中测试 Bar 类,这可能不是您想要的 - 尽管我建议无论如何为每个测试类编写一个单独的测试类,但是有这是不必要的纯粹主义的情况。

    $stubs = array();
    foreach ($array as $value) {
        $stubs[] = new MyBarTestHelper($value);
    }
    

    除此之外,我真的很惊讶你只看到当你有多个数组元素时描述的异常。我观察到 PHPUnit 实际上希望您将任何您希望它能够跟踪的方法声明为 getMock() 参数,否则会愚蠢地出错,因为本质上它在内部所做的是创建它自己的类扩展,用逻辑包装你明确声明的每个方法,让它确定它是否被调用(= 将方法名称添加到逻辑列表中)。

    所以我很天真(说真的,我可能是,我自己是一个测试新手),但看看这是否对你有任何帮助:

    $stubs = array();
    foreach ($array as $value) {
        $barStub = $this->getMock('Bar', array('GetValue'));
        $barStub->expects($this->any())
                ->method('GetValue')
                ->will($this->returnValue($value));
        $stubs[] = $barStub;
    }
    

    【讨论】:

    • 创建我自己的测试类基本上是我必须做的。在我的例子中,我可以使用 Bar 类本身,因为我使用的方法是简单的 getter,但是手工制作的模拟类可以使用更复杂的方法。
    • 关于作为第二个参数传递给getMock()的有效方法数组:根据文档,你可以省略这个参数,任何方法调用都会被允许。
    • 这也是我的直觉理解,但实际上我确实得到了错误,因为我没有明确提及调用的方法(当然只与-&gt;expects()结合使用);因此提到它。不过,我观察到的可能是一个非常特定于版本的错误主义。但是,无论哪种方式,我都很高兴您的测试按预期工作。 :)
    【解决方案2】:

    这应该满足返回一系列值的要求,如果你习惯使用 global.它不知道调用了哪个 Bar,但如果每个 Bar 按顺序由 Foo 调用一次,那么填充测试数据应该不会太难。

    $barTestData = array('empty',1,2,3,4,5,6);
    
    function barDataCallback(){
        global $barTestData;
        return next($barTestData);
    }
    

    【讨论】:

      【解决方案3】:

      我注意到您的代码中“->method('GetValue')”之后有一个额外的括号。不知道你有没有复制粘贴。

      【讨论】:

      • 修正错字,谢谢。这不是直接复制粘贴。我的代码中还有其他一些事情会搅浑水。
      • 我无法用您发布的代码重现您的问题。我认为你必须发布你实际在做什么才能得到一个好的答案。
      • 有趣。我又摸索了一些,但没有收到与上面引用的相同的错误消息。我想知道这是否是由于我必须解决的另一个错误造成的。现在我的测试似乎运行得很好,但是当它们应该通过时却失败了。
      猜你喜欢
      • 2012-01-22
      • 2011-11-10
      • 1970-01-01
      • 2014-09-04
      • 2011-12-26
      • 2011-03-06
      • 1970-01-01
      • 1970-01-01
      • 2016-08-10
      相关资源
      最近更新 更多