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:

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

This will output:

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

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.

Leave a Comment