Mock in PHPUnit - multiple configuration of the same method with different arguments Mock in PHPUnit - multiple configuration of the same method with different arguments php php

Mock in PHPUnit - multiple configuration of the same method with different arguments


Sadly this is not possible with the default PHPUnit Mock API.

I can see two options that can get you close to something like this:

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

This will work fine but you are testing more than you should (mainly that it gets called with matcher first, and that is an implementation detail).

Also this will fail if you have more than one call to each of of the functions!


Accepting both parameters and using returnCallBack

This is more work but works nicer since you don't depend on the order of the calls:

Working example:

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

This will output:

/*$ phpunit footest.phpPHPUnit 3.5.11 by Sebastian Bergmann.array(1) {  [0]=>  string(7) "Matcher"}array(1) {  [0]=>  string(6) "Logger"}.Time: 0 seconds, Memory: 3.00MbOK (1 test, 1 assertion)

I've used $this->exactly(2) in the matcher to show that this does also work with counting the invocations. If you don't need that swapping it out for $this->any() will, of course, work.


As of PHPUnit 3.6, there is $this->returnValueMap() which may be used to return different values depending on the given parameters to the method stub.


You can achieve this with a 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);    }}

Should pass for any "Logger" or "Matcher" arguments passed to the Context Mock's offsetGet method:

F:\Work\code\gordon\sandbox>phpunit NewFileTest.phpPHPUnit 3.5.13 by Sebastian Bergmann...Time: 0 seconds, Memory: 3.25MbOK (2 tests, 4 assertions)

As you can see, PHPUnit ran two tests. One for each dataProvider value. And in each of those tests it made the assertion for with() and the one for instanceOf, hence four assertions.