Using PHPUnit's returnCallback for a variable length valueMap

2013-10-15

Awkward title aside, this is just a quick post about something I rigged together the other day. I have a mocked service locator object that needed to implement returnValueMap, but where the contents of the valueMap would vary based on which test I was executing. PHPUnit does not natively support modifying the valueMap after it's been assigned, so I wanted a way to append items to the map without re-defining the whole mock for every test.

Enter returnCallback

$this->returnCallback allows you to specify just about any function to produce your mock output, including one that's locally defined.

protected $mockServiceLocator;
protected $services = array();

protected function setUp()
{
    $this->mockServiceLocator = $this->getMock('ServiceLocator');
    $this->mockServiceLocator->expects($this->any())
        ->method('get')
        ->will($this->returnCallback(array($this, 'serviceResponder')));

    $mockUbiquitousService = $this->getMock('UbiquitousService');
    $this->services['ubiquitous_service'] = $mockUbiquitousService;
}

/**
 * Fake way of allowing us to append new entries to returnValueMap
 *
 * @param string $serviceName Service name to fetch from locator
 *
 * @return PHPUnit_Framework_MockObject_MockObject
 */
public function serviceResponder($serviceName)
{
    return $this->services[$serviceName];
}

public function testServiceLocator()
{
    $mockService = $this->getMock('Service');
    $this->services['my_service'] = $mockService;
    $this->mockServiceLocator->get('my_service');
    ...
} 

In this way we're able to modify the local services property on the fly (per test) to contain any necessary mocks that we want our mocked Service Locator to provide.

Comments