php 如何让 PHPUnit MockObjects 根据参数返回不同的值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/277914/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I get PHPUnit MockObjects to return different values based on a parameter?
提问by Ben Dowling
I've got a PHPUnit mock object that returns 'return value'no matter what its arguments:
我有一个 PHPUnit 模拟对象,'return value'无论它的参数是什么,它都会返回:
// From inside a test...
$mock = $this->getMock('myObject', 'methodToMock');
$mock->expects($this->any))
->method('methodToMock')
->will($this->returnValue('return value'));
What I want to be able to do is return a different value based on the arguments passed to the mock method. I've tried something like:
我想要做的是根据传递给模拟方法的参数返回不同的值。我试过这样的事情:
$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"'));
But this causes PHPUnit to complain if the mock isn't called with the argument 'two', so I assume that the definition of methodToMock('two')overwrites the definition of the first.
但这会导致 PHPUnit 抱怨如果没有使用参数调用模拟'two',所以我假设 的定义methodToMock('two')覆盖了第一个的定义。
So my question is: Is there any way to get a PHPUnit mock object to return a different value based on its arguments? And if so, how?
所以我的问题是:有没有办法让 PHPUnit 模拟对象根据其参数返回不同的值?如果是这样,如何?
采纳答案by Howard Sandford
Use a callback. e.g. (straight from PHPUnit documentation):
使用回调。例如(直接来自 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();
// ...
}
?>
Do whatever processing you want in the callback() and return the result based on your $args as appropriate.
在 callback() 中做任何你想做的处理,并根据你的 $args 适当地返回结果。
回答by Nikola Ivancevic
From the latest phpUnit docs: "Sometimes a stubbed method should return different values depending on a predefined list of arguments. You can use returnValueMap()to create a map that associates arguments with corresponding return values."
来自最新的 phpUnit 文档:“有时,存根方法应根据预定义的参数列表返回不同的值。您可以使用returnValueMap()创建一个映射,将参数与相应的返回值相关联。”
$mock->expects($this->any())
->method('getConfigValue')
->will(
$this->returnValueMap(
array(
array('firstparam', 'secondparam', 'retval'),
array('modes', 'foo', array('Array', 'of', 'modes'))
)
)
);
回答by Adam
I had a similar problem (although slightly different... I didn't need different return value based on arguments, but had to test to ensure 2 sets of arguments were being passed to the same function). I stumbled upon using something like this:
我有一个类似的问题(虽然略有不同......我不需要基于参数的不同返回值,但必须进行测试以确保将两组参数传递给同一个函数)。我偶然发现使用了这样的东西:
$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(...));
It's not perfect, since it requires that the order of the 2 calls to foo() is known, but in practice this probably isn't toobad.
它并不完美,因为它要求 foo() 的 2 次调用的顺序是已知的,但实际上这可能还不算太糟糕。
回答by Francis Lewis
You would probably want to do a callback in a OOP fashion:
您可能希望以 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];
}
}
?>
回答by Prokhor Sednev
It is not exactly what you ask, but in some cases it can help:
这并不完全是您所要求的,但在某些情况下它可以提供帮助:
$mock->expects( $this->any() ) )
->method( 'methodToMock' )
->will( $this->onConsecutiveCalls( 'one', 'two' ) );
onConsecutiveCalls- returns a list of values in the specified order
onConsecutiveCalls- 返回指定顺序的值列表
回答by antonmarin
Pass two level array, where each element is an array of:
传递两级数组,其中每个元素是一个数组:
- first are method parameters, and least is return value.
- 首先是方法参数,最少的是返回值。
example:
例子:
->willReturnMap([
['firstArg', 'secondArg', 'returnValue']
])
回答by Gabriel Gcia Fdez
You also can return the argument as follows:
您还可以按如下方式返回参数:
$stub = $this->getMock(
'SomeClass', array('doSomething')
);
$stub->expects($this->any())
->method('doSomething')
->will($this->returnArgument(0));
As you can see in the Mocking documentation, the method returnValue($index)allows to return the given argument.
正如您在Mocking 文档中所见,该方法returnValue($index)允许返回给定的参数。
回答by eddy147
Do you mean something like this?
你的意思是这样吗?
public function TestSomeCondition($condition){
$mockObj = $this->getMockObject();
$mockObj->setReturnValue('yourMethod',$condition);
}
回答by JamShady
I had a similar problem which I couldn't work out as well (there's surprisingly little information about for PHPUnit). In my case, I just made each test separate test - known input and known output. I realised that I didn't need to make a Hyman-of-all-trades mock object, I only needed a specific one for a specific test, and thus I separated the tests out and can test individual aspects of my code as a separate unit. I'm not sure if this might be applicable to you or not, but that's down to what you need to test.
我有一个类似的问题,我也无法解决(关于 PHPUnit 的信息出奇地少)。就我而言,我只是让每个测试单独测试 - 已知输入和已知输出。我意识到我不需要制作一个万事通的模拟对象,我只需要一个特定的对象来进行特定的测试,因此我将测试分开,可以单独测试我的代码的各个方面单元。我不确定这是否适用于您,但这取决于您需要测试的内容。
回答by jjoselon
$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'),
}

