This is just a quick post to share a few small methods that I now use frequently for writing more complex tests. PHPUnit has a rather verbose way of setting mock expectations which can rapidly turn into lots of copy-pasted code. Multiple times I have tried to come up with a nice and short way to define all the different kinds of expectations in the most laconic way. So here is what I hope to be a perfectly condensed set of shorthands, lets start with usage examples first:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//Mock a class
$mock = $this->quickMock('PHPixie\Fairy');
//Mock an abstract class including all abstract methods
$mock = $this->abstractMock('PHPixie\AbstractFairy');
//Here goes:
$this->method($mock, 'hello', 5);
$mock->hello(); //5
$this->method($mock, 'hello', 5, [1, 2]);
$mock->hello(1, 2); //5
$mock->hello(3); //Fail
$this->method($mock, 'hello', 5, [1, 2], 0);
$this->method($mock, 'hello', 6, [3], 1);
$mock->hello(1, 2); //5
$mock->hello(3); //6
$f = function($x) { return $x+1;};
$this->method($mock, 'hello', $f);
$mock->hello(1); //2
$mock->hello(3); //4
//For some rare cases when you actually want to return the function
$this->method($mock, 'hello', $f, null, null, true);
$mock->hello(1); // $f
And here is the actual code, with more documentation included. Feel free to rip out these methods into your existing base tetcase.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class ExtendedTestCase extends PHPUnit_Framework_TestCase
{
/**
* Mocks a class without requiring constructor parameters
*
* @param string $class Class to mock
* @param array $methods Specific methods to mock.
* Mocks all methods by default.
*
* @return PHPUnit_Framework_MockObject_MockObject Mock instance
*/
function quickMock($class, $methods = array())
{
return $this->getMock($class, $methods, array(), '', false);
}
/**
* Mocks an abstract class without requiring constructor parameters.
* Provides a nice workaound for mocking abstract methods.
*
* @param string $class Class to mock
* @param array $methods Specific methods to mock.
* Mocks all methods by default.
*
* @return PHPUnit_Framework_MockObject_MockObject Mock instance
*/
function abstractMock($class, $methods = array())
{
if(empty($methods)){
$reflection = new \ReflectionClass($class);
foreach($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method)
$methods[]=$method->getName();
}
return $this->getMockForAbstractClass($class, array(), '', false, false, true, $methods);
}
/**
* Sets method expectations
*
* @param PHPUnit_Framework_MockObject_MockObject $mock Mocked instance
* @param string $method Method to set expectations for
* @param mixed $return What the method should return.
* If this is a Callable, e.g. a function
* then it will be teated as returnCallback ()
*
* @param array $with Array of expected arguments.
* The expectations are set to be strictly equal
* so it's safe to pass instances here.
*
* @param integer $at The position at which the method is expected to be called.
* If this is null then all other expectations will apply to all calls
*
* @param boolean $returnCallback Whether to return $return even if it is a callback.
* Used for mocking methods which may return functions.
*
* @return PHPUnit_Framework_MockObject_MockObject Mock instance
*/
function method($mock, $method, $return, $with = null, $at = null, $returnCallable = false) {
$expects = $at === null ? $this->any() : $this->at($at);
$method = $mock
->expects($expects)
->method($method);
if ($with !== null) {
foreach($with as $key => $value) {
$with[$key] = $this->identicalTo($value);
}
$method = call_user_func_array(array($method, 'with'), $with);
}
$method->will($this->returnValue($return));
if (!$returnCallable && is_callable($return)) {
$method->will($this->returnCallback($return));
}else {
$method->will($this->returnValue($return));
}
}
}
I have been using this extensively for a long time now, and find the setup to really speed up my time with PHPUnit. Hopefully it will also serve you well =)