Phpunit、mock、willReturnCallback()无法按预期工作


Phpunit, mock, willReturnCallback() doesnt work as expected

类:

class TestMe
{
    public function m1 (array &$a)
    {
    }
    public function m2 (array &$a)
    {
    }
    public function methodd()
    {
        $a = array();
        $this->m1 ($a);
        $this->m2 ($a);
        return $a;
    }
}

测试:

class X extends PHPUnit_Framework_TestCase
{
    public function testMethod()
    {
        $mock = $this->getMock('TestMe', array('m1','m2'));
        $mock->expects($this->once())->method('m1')->with(array())->willReturnCallback(function (&$x) { $x['a'] = 1; });
        $mock->expects($this->once())->method('m2')->with(array('a' => 1))->willReturnCallback(function (&$x) { $x['b'] = 2; });
        $x = $mock->methodd();
        $this->assertEquals (array('a' => 1, 'b' => 2), $x);
    }
}

不知何故它失败了:

有1个故障:

1) X::testMethod调用1次时,方法名称等于的期望值失败。调用TestMe::m1(Array(…))的参数0与预期值不匹配。断言两个数组相等失败。---预期+++实际@@@@阵列(+"a"=>1+'b'=>2)

失败!测试:1,断言:2,失败:1。

我不知道它可以是什么。换句话说,我想修改一个"reference"参数,并检查它:)

问题不直接与willReturnCallback()有关。你的问题是你在通过参考来传递东西。如果您查看失败消息,它会告诉您传递给方法m1的参数与预期不匹配。据说数组不是空的。

PHPUnit在测试运行后检查mock上的参数调用。因此,它存储了方法调用中使用的参数的副本。在回调中,您引用的是变量的引用。因此PHPUnit将自己的引用副本存储在mock中进行检查。在对函数的后续调用中,不仅会更新变量,还会更新PHPUnit存储的值。因此,当它检查mock是否用正确的参数调用时,它会对照存储在引用中的值进行检查,该值是最终值,而不是最初使用的值。

要解决此问题,请将测试更改为:

    public function testMethod()
{
    //Get a copy of the testcase to use in the callback
    $testcase = $this;
    $mock = $this->getMock('TestMe', ['m1','m2']);
    $mock->expects($this->once())
         ->method('m1')
         ->willReturnCallback(function (&$x) use ($testcase) {
             //do your parameter checking immediately
             $testcase->assertEquals([], $x); 
             $x['a'] = 1; 
         });
    $mock->expects($this->once())
         ->method('m2')
         ->willReturnCallback(function (&$x) use ($testcase) {
             //do your parameter checking immediately
             $testcase->assertEquals(['a' => 1], $x); 
             $x['b'] = 2; 
         });
    $x = $mock->method();
    $this->assertEquals (['a' => 1, 'b' => 2], $x);
}

尽管是IMO,但您不应该嘲笑您正在测试的类中的代码。methodd()使用这些函数的事实是类的实现细节。如果您决定将m1()m2中的逻辑移到methodd()中,那么您的测试应该会通过。模拟这些方法会降低测试的有用性。它们可能会因为类中的代码更改而失败,而不是因为功能/错误中的实际更改。

无论如何,解决特定问题的方法是检查回调中的参数值,而不是使用with()