如何获取PHPUnit MockObjects以基于参数返回不同的值?


141

我有一个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"'));

但这会导致PHPUnit抱怨,如果未使用参数调用模拟'two',那么我认为methodToMock('two')覆盖的定义会覆盖第一个的定义。

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

Answers:


125

使用回调。例如(直接来自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返回结果。


2
您可以提供文档链接吗?我似乎无法通过“ Google”找到它
Kris Erickson

6
请注意,您可以通过传递数组(例如)来将方法用作回调$this->returnCallback(array('MyClassTest','myCallback'))
帕特里克·费舍尔

1
也应该可以直接将关闭传递给它
Ocramius

7
仅应在极少数情况下使用。我建议改用returnValueMap,因为它不需要在回调中编写自定义逻辑。
Herman J. Radtke III

1
我感激不尽。另外,对于PHP版本> 5.4,您可以使用匿名函数作为回调。$this->returnCallback(function() { // ... })
bmorenate

110

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

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

3
帖子中的链接很旧,正确的链接在这里:returnValueMap()
hejdav

48

我有一个类似的问题(尽管略有不同……我不需要基于参数的不同返回值,但是必须进行测试以确保将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()的顺序是已知的,但在实践中,这可能不是糟糕。


28

您可能希望以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, 'returnCallback'));

        $object->returnAction('param1');
        // assert what param1 should return here

        $object->returnAction('param2');
        // assert what param2 should return here
    }

    public function returnCallback()
    {
        $args = func_get_args();

        // process $args[0] here and return the data you want to mock
        return 'The parameter was ' . $args[0];
    }
}
?>


4

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

  • 第一个是方法参数,最小的是返回值。

例:

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

2

您还可以返回以下参数:

$stub = $this->getMock(
  'SomeClass', array('doSomething')
);

$stub->expects($this->any())
     ->method('doSomething')
     ->will($this->returnArgument(0));

如您在Mocking文档中所见,该方法returnValue($index)允许返回给定的参数。


0

你的意思是这样吗?

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

我认为这是SimpleTest代码,而不是PHPUnit。但是,这不是我想要实现的目标。假设我有一个模拟对象,该对象返回了给定数字的单词。我的模拟方法需要在调用1时返回“一个”,在调用2等时返回“两个”。$
Ben Dowling

0

我有一个类似的问题,我也无法解决(关于PHPUnit的信息少得令人惊讶)。就我而言,我只是将每个测试单独进行了测试-已知输入和已知输出。我意识到我不需要制作万能的模拟对象,只需要为特定的测试指定一个特定的对象,因此我将测试分离出来,可以将代码的各个方面作为一个单独的对象进行测试单元。我不确定这是否适用于您,但这取决于您需要测试的内容。


不幸的是,在我的情况下这是行不通的。模拟被传递到我正在测试的方法中,并且测试方法使用不同的参数调用模拟的方法。有趣的是,您无法解决问题。听起来这可能是PHPUnit的限制。
本·道林

-1
$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'),


    }

-2

尝试:

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

这个答案并不适用于原来的问题,但它详细介绍了类似的问题,我有:验证一个特定的参数设置。PHPUnit的with()接受多个参数,每个参数一个匹配器。
TaZ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.