模拟PHPUnit – 使用不同参数的相同方法的多重configuration

这样可以configurationPHPUnit模拟吗?

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

我使用PHPUnit 3.5.10,因为它期望“logging器”参数,所以我要求匹配器时失败。 这就像第二个期望是重写第一个,但是当我转储模拟,一切都看起来不错。

从PHPUnit 3.6起,有$this->returnValueMap() ,它可以用来根据方法存根的给定参数返回不同的值。

可惜的是,这对默认的PHPUnit Mock API来说是不可能的。

我可以看到两个选项可以让你接近这样的事情:

使用 – > at($ x)

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

这将工作正常,但你正在testing比你更应该(主要是它被调用与匹配器第一,这是一个实现的细节)。

如果你有多个对每个函数的调用,这也会失败!


接受这两个参数并使用returnCallBack

这是更多的工作,但更好,因为你不依赖于电话的顺序:

工作示例:

 <?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"; } } 

这将输出:

 /* $ 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) 

我已经在匹配器中使用了$this->exactly(2) this- $this->exactly(2)来表明这也适用于计数调用。 如果你不需要把它换成$this->any()将会工作。

你可以用callback来实现这个:

 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); } } 

应传递任何传递给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) 

正如你所看到的,PHPUnit运行了两个testing。 一个用于每个dataProvider值。 在每个testing中,它都with()和assertion with()来表示断言。

从@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'); 

这是PHPUnit 3.7。

如果您正在testing的方法实际上没有返回任何内容,并且只需要testing是否使用了正确的参数来调用该方法,则同样适用。 对于这种情况,我也试图用$ this-> callback的callback函数作为with的参数,而不是will中的returnCallback。 这失败了,因为内部phpunit在validation参数匹配器callback的过程中两次调用callback。 这意味着该方法在第二次调用时失败,该参数已经从期望的参数数组中移除。 我不知道为什么phpunit调用它两次(似乎是一个不必要的浪费),我想你可以通过只在第二个调用删除它的工作,但我没有足够的信心,这是有意和一致的phpunit行为依靠发生。

我只是偶然发现这个PHP扩展模拟对象: https : //github.com/etsy/phpunit-extensions/wiki/Mock-Object

我的2美分的话题:在使用($ x)时要注意:这意味着期望的方法调用将是模拟对象上的($ x + 1)方法调用; 这并不意味着这将是预期方法的($ x + 1)调用。 这让我浪费了一些时间,所以我希望它不会和你在一起。 亲切的问候给大家。