如何让 PHPUnit MockObjects 根据参数返回不同的值?

我有一个 PHPUnit 模拟对象,不管它的参数是什么,它都返回 'return value':

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

我希望能够根据传递给 mock 方法的参数返回一个不同的值。我试过这样的方法:

$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'调用 mock,这会导致 PHPUnit 抱怨,所以我假设 methodToMock('two')的定义覆盖了第一个的定义。

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

101933 次浏览

你是说像这样的东西吗?

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

我也遇到过类似的问题,但是我不能很好地解决(关于 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 返回结果。

试试:

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

我也遇到过类似的问题(尽管稍有不同... ... 但我并不需要基于参数的不同返回值,而是必须进行测试以确保将两组参数传递给同一个函数)。我偶然使用了这样的东西:

$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(...));

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

您可能希望以 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];
}
}
?>

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

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

您还可以返回以下参数:

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


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

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

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

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

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

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

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

例如:

->willReturnMap([
['firstArg', 'secondArg', 'returnValue']
])
$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'),




}