Vác*_*tný 49 php phpunit mocking
是否可以以这种方式配置PHPUnit mock?
$context = $this->getMockBuilder('Context')
->getMock();
$context->expects($this->any())
->method('offsetGet')
->with('Matcher')
->will($this->returnValue(new Matcher()));
$context->expects($this->any())
->method('offsetGet')
->with('Logger')
->will($this->returnValue(new Logger()));
Run Code Online (Sandbox Code Playgroud)
我使用PHPUnit 3.5.10,当我要求Matcher时它会失败,因为它需要"Logger"参数.这就像第二个期望是重写第一个,但是当我转储模拟时,一切看起来都不错.
edo*_*ian 61
遗憾的是,使用默认的PHPUnit Mock API是不可能的.
我可以看到两个可以让你接近这样的选项:
$context = $this->getMockBuilder('Context')
->getMock();
$context->expects($this->at(0))
->method('offsetGet')
->with('Matcher')
->will($this->returnValue(new Matcher()));
$context->expects($this->at(1))
->method('offsetGet')
->with('Logger')
->will($this->returnValue(new Logger()));
Run Code Online (Sandbox Code Playgroud)
这样可以正常工作,但是你测试的比你应该更多(主要是先用matcher调用它,这是一个实现细节).
如果您对每个功能进行多次调用,这也将失败!
这是更多的工作,但更好,因为你不依赖于调用的顺序:
<?php
class FooTest extends PHPUnit_Framework_TestCase {
public function testX() {
$context = $this->getMockBuilder('Context')
->getMock();
$context->expects($this->exactly(2))
->method('offsetGet')
->with($this->logicalOr(
$this->equalTo('Matcher'),
$this->equalTo('Logger')
))
->will($this->returnCallback(
function($param) {
var_dump(func_get_args());
// The first arg will be Matcher or Logger
// so something like "return new $param" should work here
}
));
$context->offsetGet("Matcher");
$context->offsetGet("Logger");
}
}
class Context {
public function offsetGet() { echo "org"; }
}
Run Code Online (Sandbox Code Playgroud)
这将输出:
/*
$ phpunit footest.php
PHPUnit 3.5.11 by Sebastian Bergmann.
array(1) {
[0]=>
string(7) "Matcher"
}
array(1) {
[0]=>
string(6) "Logger"
}
.
Time: 0 seconds, Memory: 3.00Mb
OK (1 test, 1 assertion)
Run Code Online (Sandbox Code Playgroud)
我$this->exactly(2)在匹配器中使用过来表明这也适用于计算调用.如果你不需要将它换成$this->any()遗嘱,当然是工作.
lee*_*eeb 29
从PHPUnit 3.6开始,$this->returnValueMap()可以使用它根据给定方法存根的给定参数返回不同的值.
您可以通过回调实现此目的:
class MockTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideExpectedInstance
*/
public function testMockReturnsInstance($expectedInstance)
{
$context = $this->getMock('Context');
$context->expects($this->any())
->method('offsetGet')
// Accept any of "Matcher" or "Logger" for first argument
->with($this->logicalOr(
$this->equalTo('Matcher'),
$this->equalTo('Logger')
))
// Return what was passed to offsetGet as a new instance
->will($this->returnCallback(
function($arg1) {
return new $arg1;
}
));
$this->assertInstanceOf(
$expectedInstance,
$context->offsetGet($expectedInstance)
);
}
public function provideExpectedInstance()
{
return array_chunk(array('Matcher', 'Logger'), 1);
}
}
Run Code Online (Sandbox Code Playgroud)
应该传递给Context Mock offsetGet方法的任何"Logger"或"Matcher"参数:
F:\Work\code\gordon\sandbox>phpunit NewFileTest.php
PHPUnit 3.5.13 by Sebastian Bergmann.
..
Time: 0 seconds, Memory: 3.25Mb
OK (2 tests, 4 assertions)
Run Code Online (Sandbox Code Playgroud)
如您所见,PHPUnit运行了两个测试.每个dataProvider值一个.在每个测试中,它都为断言with()和断言做出了instanceOf,因此有四个断言.
接下来是@edorian的回答和评论(@MarijnHuizendveld)关于确保用Matcher和Logger调用方法,而不是简单地用Matcher或Logger调用两次,这是一个例子.
$expectedArguments = array('Matcher', 'Logger');
$context->expects($this->exactly(2))
->method('offsetGet')
->with($this->logicalOr(
$this->equalTo('Matcher'),
$this->equalTo('Logger')
))
->will($this->returnCallback(
function($param) use (&$expectedArguments){
if(($key = array_search($param, $expectedArguments)) !== false) {
// remove called argument from list
unset($expectedArguments[$key]);
}
// The first arg will be Matcher or Logger
// so something like "return new $param" should work here
}
));
// perform actions...
// check all arguments removed
$this->assertEquals(array(), $expectedArguments, 'Method offsetGet not called with all required arguments');
Run Code Online (Sandbox Code Playgroud)
这是PHPUnit 3.7.
如果您正在测试的方法实际上没有返回任何内容,并且您只需要测试使用正确的参数调用它,则应用相同的方法.对于这种情况,我还尝试使用$ this-> callback的回调函数作为with的参数,而不是遗嘱中的returnCallback.这失败了,因为内部phpunit 两次调用回调在验证参数匹配器回调的过程中.这意味着该方法在第二次调用时失败,该参数已从预期的arguments数组中删除.我不知道为什么phpunit调用它两次(似乎是不必要的浪费),我猜你可以通过在第二次调用时删除它来解决这个问题,但我没有足够的信心这是有意和一致的phpunit行为依靠那种情况发生.