A better mock object builder for PHPUnit
When working with mocks for unit tests, you're going to write a lot of code for the mocks. There are a lot of functions you're going to need to call every single time. To get around this you can use the following utility function[1]:
/**
* Get Mock object
*
* @param string $class Class to mock with namespace
* @param array|null $methods Methods to mock with key return => return value and key call with ether once, any, or a numeric value which has to be matched exactly
* @param array|null Sets the mock constructor args or disables mock constructor when null is given
*
* @return \PHPUnit_Framework_MockObject_MockObject Mock object
*
* @codeCoverageIgnore
*/
public function getMockObject($class, $methods, $construct = null)
{
/** @var \PHPUnit_Framework_MockObject_MockBuilder */
$mockBuilder = $this->getMockBuilder($class)
->setMethods(array_keys($methods))
->disableOriginalConstructor();
if($construct === null) {
$mockBuilder->disableOriginalConstructor();
} elseif(!empty($construct)) {
$mockBuilder->setConstructorArgs($construct);
}
$mock = $mockBuilder->getMock();
foreach($methods as $method => $definition) {
if(is_null($definition)) {
continue;
}
if(isset($definition['return_type'])) {
switch($definition['return_type']) {
case 'value_map':
$return = $this->returnValueMap($definition['return']);
break;
case 'callback':
$return = $this->returnCallback($definition['return']);
break;
case 'consecutive':
$return = call_user_func_array(array($this, 'onConsecutiveCalls'), $definition['return']);
break;
case 'exception':
$return = $this->throwException($definition['return']);
break;
default:
$return = $this->returnValue($definition['return']);
break;
}
} else {
$return = $this->returnValue($definition['return']);
}
if(isset($definition['call'])) {
switch($definition['call']) {
case 'once':
$call = $this->once();
break;
case 'any':
$call = $this->any();
break;
case 'at':
// return should contain array with "at" values
break;
default:
if(is_numeric($definition['call'])) {
$call = $this->exactly($definition['call']);
} else {
$call = $this->any();
}
break;
}
} else {
$call = $this->any();
}
// Add at-matchers
if(isset($definition['call']) && $definition['call'] == 'at') {
foreach($definition['return'] as $at => $value) {
$mock->expects($this->at($at))->method($method)->will($this->returnValue($value));
}
} else {
$mock->expects($call)->method($method)->will($return);
}
}
return $mock;
}
With this you be able to convert the following code
$mockBuilder = $this
->getMockBuilder('Liplex\Backend\Adapter')
->setMethods(['manageRights'])
->disableOriginalConstructor();
$mock = $mockBuilder->getMock();
$mock
->expects($this->any())
->method('manageRights')
->will($this->returnValue($response));
into the following:
$mock = $this->getMockObject(
'Liplex\Backend\Adapter',
[
'manageRights' => [
'return' => $response
]
]
);
Code by Tschela Baumann ↩︎