Developer Blog

  • Blog
  • /
  • PHPUnit Shorthand Functions
By Dracony on 15 October 2014

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 =)

comments powered by Disqus