46

我正在尝试使用 PHPUnit 的returnValueMap()来存根读取的结果。它没有产生预期的结果,但等效的 returnCallback() 可以。如果您想自己检查,我已经提供了我的测试用例。

返回值映射()

$enterprise = $this->getMock('Enterprise', array('field'));
$enterprise->expects($this->any())
    ->method('field')
    ->will($this->returnValueMap(array(
        array('subscription_id', null),
        array('name', 'Monday Farms')
    )));
$enterprise->subscribe('basic');

结果:

Subscription ID: NULL
Name: NULL

返回回调()

$enterprise = $this->getMock('Enterprise', array('field'));
$enterprise->expects($this->any())
    ->method('field')
    ->will($this->returnCallback(function ($arg) {
        $map = array(
            'subscription_id' => null,
            'name' => 'Monday Farms'
        );
        return $map[$arg];
    }));
$enterprise->subscribe('basic');

结果:

Subscription ID: NULL
Name: string(12) "Monday Farms"

企业::订阅

public function subscribe() {
    echo 'Subscription ID: ';
    var_dump($this->field('subscription_id'));
    echo 'Name: ';
    var_dump($this->field('name'));
}

为什么 returnValueMap() 不能按我的预期工作?我到底错过了什么?

4

3 回答 3

120

我遇到了同样的问题,最终发现 returnValueMap() 必须映射函数的所有参数,包括可选参数,然后是所需的返回值。

Zend 框架的示例函数:

public function getParam($key, $default = null)
{
    $key = (string) $key;
    if (isset($this->_params[$key])) {
        return $this->_params[$key];
    }

    return $default;
}

必须像这样映射:

$request->expects($this->any())
        ->method('getParam')
        ->will($this->returnValueMap(array(array($param, null, $value))));

如果没有中间的 null,它将无法工作。

于 2013-03-08T18:16:57.597 回答
9

我一直在寻找同样的问题,最终出于绝望 xdebug-stepped through Framework/MockObject/Stub/ReturnValueMap.php 和 Framework/MockObject/InvocationMocker.php [in method InvocationMocker::invoke(PHPUnit_Framework_MockObject_Invocation $invocation)],和我观察到以下几点:

  1. 当调用存根函数时,您提供的映射数组中的值不仅必须与预期的参数相同,而且它们必须是相同的类型。这是因为在 ReturnValueMap::invoke() 方法中的 Framework/MockObject/Stub/ReturnValueMap.php 内部,在第 75 行比较了提供的参数和预期的参数,如下所示:

    if ($invocation->parameters === $map) {
    

    所以,预期的结果

    $mock->myStubbedMethod( "1", "2" )
    

    使用地图数组

    array(
        array( 1, 1, 500 ),
        array( 1, 2, 700 )
    )
    

    会失望的。相反,结果将是 NULL。

  2. 在更微妙的一点上,您可能已经使用两种不同的模拟场景来存根该方法(就像我所做的那样 - 是的,我很傻!)

    因此,为了阐明,第一个模拟存根可以包含

    $mock->expects( $this->any() )
           ->method('myStubbedMethod')
           ->will( $this->returnValue(750) );
    

    然后,在相同的单元测试方法中,它可以包含

    $arrMap = array(
        array( 1, 1, 500 ),
        array( 1, 2, 700 ),
        array( 2, 3, 1500 ),
    );
    $mock->expects( $this->any() )
            ->method('myStubbedMethod')
            ->will( $this->returnValueMap($arrMap) );
    

    当调用 stubbed-method 时,将实现 map-array 版本。这是显而易见且不言而喻的,然而,当你在热火朝天的时候编码,并且在你开发时在你的脑海中区分不同的代码行为时,它很容易被忽视。

于 2015-05-27T12:38:51.730 回答
0

问题似乎出在代码中的其他地方,或者可能是旧 PHPUnit 版本的问题。

对我来说,它使用以下代码工作:

cat EnterpriseTest.php

<?php

class EnterpriseTest extends PHPUnit_Framework_TestCase {

    public function testReturnValueMap() {
        $enterprise = $this->getMock('Enterprise', array('field'));
        $enterprise->expects($this->any())
            ->method('field')
            ->will($this->returnValueMap(array(
                array('subscription_id', null),
                array('name', 'Monday Farms')
            )))
        ;
        $enterprise->subscribe('basic');        
    }

}

class Enterprise {

    public function field($name) {
    }

    public function subscribe() {
        echo 'Subscription ID: ';
        var_dump($this->field('subscription_id'));
        echo 'Name: ';
        var_dump($this->field('name'));
    }

}

输出:

phpunit-dev EnterpriseTest.php 
PHPUnit 3.7.6 by Sebastian Bergmann.

.Subscription ID: NULL
Name: string(12) "Monday Farms"


Time: 0 seconds, Memory: 6.75Mb

OK (1 test, 1 assertion)

所以虽然我不知道为什么它不起作用,但我至少可以告诉你,你做得对,而且你理解returnValueMap正确:)

于 2012-10-08T09:26:27.343 回答