web-dev-qa-db-ja.com

PHPUnitのreturnValueMapが予期した結果を生成しない

PHPUnitの returnValueMap() を使用して、読み取り結果をスタブ化しようとしています。期待どおりの結果は得られませんが、同等のreturnCallback()で得られます。自分で検査したい場合は、 test case を使用できるようにしました。

returnValueMap()

$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

returnCallback()

$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"

Enterprise :: subscribe

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

ReturnValueMap()が期待どおりに機能しないのはなぜですか?正確には何が欠けていますか?

39
Brad Koch

同じ問題があり、最終的にreturnValueMap()が関数のすべてのパラメーターオプションのパラメーターを含むをマップし、次に目的の戻り値をマップする必要があることがわかりました。

Zend Frameworkの関数の例:

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

途中にヌルがないと動作しません。

101
Marco Roy

私はこれと同じ問題を突き止め、結局、Framework/MockObject/Stub/ReturnValueMap.phpとFramework/MockObject/InvocationMocker.php [メソッドInvocationMocker :: invoke(PHPUnit_Framework_MockObject_Invocation $ invocation)]を介してxdebug-steppedして、必死になって、そして次の点を確認しました。

  1. 指定したmap-arrayの値は、スタブ関数が呼び出されたときに予期されるパラメーターと同じであるだけでなく、同じタイプである必要があります。これは、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. もっと微妙な点で、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バージョンが実装されます。これは明白で自明ですが、猛烈な勢いでコーディングしているとき、および開発中にさまざまなコードの振る舞いを心の中で分離しているときは、見落とされがちです。

8
user3125602

問題はコードのどこかにあるようであるか、古い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を理解していることを伝えることができます:)

0
edorian