【问题标题】:How can I get PHPUnit MockObjects to return different values based on a parameter?如何让 PHPUnit MockObjects 根据参数返回不同的值?
【发布时间】:2010-09-21 15:09:29
【问题描述】:

我有一个 PHPUnit 模拟对象,无论它的参数是什么,它都会返回 'return value'

// From inside a test...
$mock = $this->getMock('myObject', 'methodToMock');
$mock->expects($this->any))
     ->method('methodToMock')
     ->will($this->returnValue('return value'));

我希望能够根据传递给模拟方法的参数返回不同的值。我尝试过类似的方法:

$mock = $this->getMock('myObject', 'methodToMock');

// methodToMock('one')
$mock->expects($this->any))
     ->method('methodToMock')
     ->with($this->equalTo('one'))
     ->will($this->returnValue('method called with argument "one"'));

// methodToMock('two')
$mock->expects($this->any))
     ->method('methodToMock')
     ->with($this->equalTo('two'))
     ->will($this->returnValue('method called with argument "two"'));

但是如果没有使用参数'two' 调用模拟,这会导致PHPUnit 抱怨,所以我假设methodToMock('two') 的定义会覆盖第一个的定义。

所以我的问题是:有没有办法让 PHPUnit 模拟对象根据其参数返回不同的值?如果有,怎么做?

【问题讨论】:

    标签: php unit-testing mocking phpunit


    【解决方案1】:

    你的意思是这样的吗?

    public function TestSomeCondition($condition){
      $mockObj = $this->getMockObject();
      $mockObj->setReturnValue('yourMethod',$condition);
    }
    

    【讨论】:

    • 我认为那是 SimpleTest 代码,而不是 PHPUnit。但是不,这不是我想要实现的。假设我有一个模拟对象,它返回一个给定数字的单词。我的模拟方法需要在使用 1 调用时返回“一”,在使用 2 调用时返回“二”等。$
    【解决方案2】:

    我遇到了一个类似的问题,但我也无法解决(关于 PHPUnit 的信息非常少)。就我而言,我只是将每个测试单独测试 - 已知输入和已知输出。我意识到我不需要制作一个万事通的模拟对象,我只需要一个用于特定测试的特定对象,因此我将测试分开并可以单独测试我的代码的各个方面单元。我不确定这是否适用于您,但这取决于您需要测试的内容。

    【讨论】:

    • 不幸的是,这在我的情况下不起作用。模拟被传递到我正在测试的方法中,并且测试方法使用不同的参数调用模拟方法。有趣的是,您无法解决问题。听起来这可能是 PHPUnit 的限制。
    【解决方案3】:

    使用回调。例如(直接来自 PHPUnit 文档):

    <?php
    class StubTest extends PHPUnit_Framework_TestCase
    {
        public function testReturnCallbackStub()
        {
            $stub = $this->getMock(
              'SomeClass', array('doSomething')
            );
    
            $stub->expects($this->any())
                 ->method('doSomething')
                 ->will($this->returnCallback('callback'));
    
            // $stub->doSomething() returns callback(...)
        }
    }
    
    function callback() {
        $args = func_get_args();
        // ...
    }
    ?>
    

    在 callback() 中做任何你想做的处理,并根据你的 $args 适当地返回结果。

    【讨论】:

    • 您能提供文档的链接吗?我似乎无法通过“the Google”找到它
    • 请注意,您可以通过传递数组将方法用作回调,例如$this-&gt;returnCallback(array('MyClassTest','myCallback')).
    • 也应该可以直接给它传递一个闭包
    • 这应该只在极少数情况下使用。我建议改用returnValueMap,因为它不需要在回调中编写自定义逻辑。
    • 感激不尽。此外,对于 PHP 版本 > 5.4,您可以使用匿名函数作为回调。 $this-&gt;returnCallback(function() { // ... })
    【解决方案4】:

    试试:

    ->with($this->equalTo('one'),$this->equalTo('two))->will($this->returnValue('return value'));
    

    【讨论】:

    • 这个答案不适用于原始问题,但它详细说明了我遇到的类似问题:验证是否提供了某个 set 参数。 PHPUnit 的 with() 接受多个参数,每个参数一个匹配器。
    【解决方案5】:

    我遇到了类似的问题(虽然略有不同......我不需要基于参数的不同返回值,但必须进行测试以确保将 2 组参数传递给同一个函数)。我偶然发现使用这样的东西:

    $mock = $this->getMock();
    $mock->expects($this->at(0))
        ->method('foo')
        ->with(...)
        ->will($this->returnValue(...));
    
    $mock->expects($this->at(1))
        ->method('foo')
        ->with(...)
        ->will($this->returnValue(...));
    

    这并不完美,因为它要求知道 2 次调用 foo() 的顺序,但在实践中这可能不是 糟糕。

    【讨论】:

    • at() 匹配器已被弃用。它将在 PHPUnit 10 中删除。请重构您的测试,使其不依赖于调用方法的顺序。
    【解决方案6】:

    您可能希望以 OOP 方式进行回调:

    <?php
    class StubTest extends PHPUnit_Framework_TestCase
    {
        public function testReturnAction()
        {
            $object = $this->getMock('class_name', array('method_to_mock'));
            $object->expects($this->any())
                ->method('method_to_mock')
                ->will($this->returnCallback(array($this, 'returnTestDataCallback')));
    
            $object->returnAction('param1');
            // assert what param1 should return here
    
            $object->returnAction('param2');
            // assert what param2 should return here
        }
        
        public function returnTestDataCallback()
        {
            $args = func_get_args();
    
            // process $args[0] here and return the data you want to mock
            return 'The parameter was ' . $args[0];
        }
    }
    ?>
    

    【讨论】:

      【解决方案7】:

      来自最新的 phpUnit 文档:“有时,存根方法应根据预定义的参数列表返回不同的值。您可以使用 returnValueMap() 创建将参数与相应返回值相关联的映射。”

      $mock->expects($this->any())
          ->method('getConfigValue')
          ->will(
              $this->returnValueMap(
                  array(
                      array('firstparam', 'secondparam', 'retval'),
                      array('modes', 'foo', array('Array', 'of', 'modes'))
                  )
              )
          );
      

      【讨论】:

      【解决方案8】:

      您也可以按如下方式返回参数:

      $stub = $this->getMock(
        'SomeClass', array('doSomething')
      );
      
      $stub->expects($this->any())
           ->method('doSomething')
           ->will($this->returnArgument(0));
      

      正如您在Mocking documentation 中看到的,returnValue($index) 方法允许返回给定的参数。

      【讨论】:

      • 我认为 Stub vs Mock 已经让很多人感到困惑。你通过$stub = $this-&gt;getMock( 让它变得更加混乱
      【解决方案9】:

      这并不完全符合您的要求,但在某些情况下它可以提供帮助:

      $mock->expects( $this->any() ) )
       ->method( 'methodToMock' )
       ->will( $this->onConsecutiveCalls( 'one', 'two' ) );
      

      onConsecutiveCalls - 按指定顺序返回值列表

      【讨论】:

        【解决方案10】:

        传递两级数组,其中每个元素是一个数组:

        • 首先是方法参数,最后是返回值。

        示例:

        ->willReturnMap([
            ['firstArg', 'secondArg', 'returnValue']
        ])
        

        【讨论】:

          【解决方案11】:
          $this->BusinessMock = $this->createMock('AppBundle\Entity\Business');
          
              public function testBusiness()
              {
                  /*
                      onConcecutiveCalls : Whether you want that the Stub returns differents values when it will be called .
                  */
                  $this->BusinessMock ->method('getEmployees')
                                          ->will($this->onConsecutiveCalls(
                                                      $this->returnArgument(0),
                                                      $this->returnValue('employee')                                      
                                                      )
                                                );
                  // first call
          
                  $this->assertInstanceOf( //$this->returnArgument(0),
                          'argument',
                          $this->BusinessMock->getEmployees()
                          );
                 // second call
          
          
                  $this->assertEquals('employee',$this->BusinessMock->getEmployees()) 
                //$this->returnValue('employee'),
          
          
              }
          

          【讨论】:

            猜你喜欢
            • 2017-11-30
            • 2013-11-30
            • 2021-06-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-01-12
            • 1970-01-01
            • 2018-07-06
            相关资源
            最近更新 更多