Tag Archives: phpunit

Asserting the Output of Complex Arrays in PHPUnit/PHPStorm

In unit testing, have you ever had assert a really complex array that you didn’t really want to generate the whole expected array yourself? Such as:

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
array (
  0 => 
  array (
    0 => 'var 0-0',
    1 => 'var 0-1',
    2 => 'var 0-2',
    3 => 'var 0-3',
    4 => 'var 0-4',
    5 => 'var 0-5',
    6 => 'var 0-6',
    7 => 'var 0-7',
    8 => 'var 0-8',
    9 => 'var 0-9',
  ),
  1 => 
  array (
    0 => 'var 1-0',
    1 => 'var 1-1',
    2 => 'var 1-2',
    3 => 'var 1-3',
    4 => 'var 1-4',
    5 => 'var 1-5',
    6 => 'var 1-6',
    7 => 'var 1-7',
    8 => 'var 1-8',
    9 => 'var 1-9',
  ),
  2 => 
  array (
    0 => 'var 2-0',
    1 => 'var 2-1',
    2 => 'var 2-2',
    3 => 'var 2-3',
    4 => 'var 2-4',
    5 => 'var 2-5',
    6 => 'var 2-6',
    7 => 'var 2-7',
    8 => 'var 2-8',
    9 => 'var 2-9',
  ),
  3 => 
  array (
    0 => 'var 3-0',
    1 => 'var 3-1',
    2 => 'var 3-2',
    3 => 'var 3-3',
    4 => 'var 3-4',
    5 => 'var 3-5',
    6 => 'var 3-6',
    7 => 'var 3-7',
    8 => 'var 3-8',
    9 => 'var 3-9',
  ),
  4 => 
  array (
    0 => 'var 4-0',
    1 => 'var 4-1',
    2 => 'var 4-2',
    3 => 'var 4-3',
    4 => 'var 4-4',
    5 => 'var 4-5',
    6 => 'var 4-6',
    7 => 'var 4-7',
    8 => 'var 4-8',
    9 => 'var 4-9',
  ),
  5 => 
  array (
    0 => 'var 5-0',
    1 => 'var 5-1',
    2 => 'var 5-2',
    3 => 'var 5-3',
    4 => 'var 5-4',
    5 => 'var 5-5',
    6 => 'var 5-6',
    7 => 'var 5-7',
    8 => 'var 5-8',
    9 => 'var 5-9',
  ),
  6 => 
  array (
    0 => 'var 6-0',
    1 => 'var 6-1',
    2 => 'var 6-2',
    3 => 'var 6-3',
    4 => 'var 6-4',
    5 => 'var 6-5',
    6 => 'var 6-6',
    7 => 'var 6-7',
    8 => 'var 6-8',
    9 => 'var 6-9',
  ),
  7 => 
  array (
    0 => 'var 7-0',
    1 => 'var 7-1',
    2 => 'var 7-2',
    3 => 'var 7-3',
    4 => 'var 7-4',
    5 => 'var 7-5',
    6 => 'var 7-6',
    7 => 'var 7-7',
    8 => 'var 7-8',
    9 => 'var 7-9',
  ),
  8 => 
  array (
    0 => 'var 8-0',
    1 => 'var 8-1',
    2 => 'var 8-2',
    3 => 'var 8-3',
    4 => 'var 8-4',
    5 => 'var 8-5',
    6 => 'var 8-6',
    7 => 'var 8-7',
    8 => 'var 8-8',
    9 => 'var 8-9',
  ),
  9 => 
  array (
    0 => 'var 9-0',
    1 => 'var 9-1',
    2 => 'var 9-2',
    3 => 'var 9-3',
    4 => 'var 9-4',
    5 => 'var 9-5',
    6 => 'var 9-6',
    7 => 'var 9-7',
    8 => 'var 9-8',
    9 => 'var 9-9',
  ),
  10 => 
  array (
    0 => 'var 10-0',
    1 => 'var 10-1',
    2 => 'var 10-2',
    3 => 'var 10-3',
    4 => 'var 10-4',
    5 => 'var 10-5',
    6 => 'var 10-6',
    7 => 'var 10-7',
    8 => 'var 10-8',
    9 => 'var 10-9',
  )

(I don’t want to type that up by hand!)

Well, guess what? You can be lazy about it… Simply use var_export and php will generate all the code for you to straight copy and paste.

1
var_export($complexObj);

**DISCLAIMER WARNING** This is generally a VERY bad practice. You are always suppose to create the assertions independently from what the output generates. It will be very easy to just copy and paste a mistake in the output that is not correct. Be sure if you do this that you look very carefully at the output and make sure it is exactly what it is supposed to be.

PHPUnit Mocking

Mocking is great for very lean unit tests. It allows you to setup a repeatable scenario to test as many of the states of a unit as you want. In general, I want to be testing only a single method or a single class. If you’re testing more than this, you’re not writing unit tests – you’re writing integration tests! A good measure on if you are writing proper unit tests would be to change something in the code to make a test fail. If you end up with a cascade of tests fail, you have integration tests, not unit tests!

So in unit testing, you want to be able to isolate a single unit and manipulate the inputs to cover all types of conditions and verify the validity of the outputs to those conditions. I like to think of it like an algebraic inequality problem. You always check your boundaries and then a value within the different boundary regions. Think of it similarly with unit testing.

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
// Calculator.php
<?php
class Calculator {
    public getNumberFromUserInput() {
        // complicated function to get number from user input 
    }
 
    public divideBy($num2) {
        return $this->getNumberFromUserInput()/$num2;
    }
}
 
// CalculatorTest.php
<?php
 
include_once("Calculator.php");
 
class CalculatorTest extends \PHPUnit_Framework_TestCase {
    public function testDivideByPositiveNumber() {
        $calcMock=$this->getMock('\Calculator',array('getNumberFromUserInput'));
        $calcMock->expects($this->once())
            ->method('getNumberFromUserInput')
            ->will($this->returnValue(10));
        $this->assertEquals(5,$calcMock->divideBy(2));
    }
 
    public function testDivideByZero() {
        $calcMock=$this->getMock('\Calculator',array('getNumberFromUserInput'));
        $calcMock->expects($this->once())
            ->method('getNumberFromUserInput')
            ->will($this->returnValue(10));
        $this->assertEquals(NAN, $calcMock->divideBy(0));
 
    }
 
    public function testDivideByNegativeNumber() {
        $calcMock=$this->getMock('\Calculator',array('getNumberFromUserInput'));
        $calcMock->expects($this->once())
            ->method('getNumberFromUserInput')
            ->will($this->returnValue(10));
        $this->assertEquals(-2,$calcMock->divideBy(-5));
 
    }
}

As you can see with the example, sometimes inputs and outputs of functions are not always so straight-forward. While we can have our standard input and output passed from the method parameters and the return value, more often than not, inputs and outputs are received and sent by calling other functions. With mocking though, this is no issue. We can mock any function and  define exactly what it should return. We can also test and verify that the function was called.

Let’s break down the example.

1
$calcMock=$this->getMock('\Calculator',array('getNumberFromUserInput'));

We are creating a new Calculator mock. The first parameter tells phpunit what class to mock, the  2nd parameter tells phpunit to only mock the ‘getNumberFromUserInput’ function and not anything else. We need to use the real divideBy() function to test that. getMock() has a lot of useful options, refer to the api or Mark Mzyk’s blog post I found on the getMock method signatures.

The next line(s) set up the mocked function.

1
2
3
$calcMock->expects($this->once())
            ->method('getNumberFromUserInput')
            ->will($this->returnValue(10));

Line 1: We are saying here that we expect this method to be called once. If it is called less or more than once, we will get an exception.

Line 2: The method name we are mocking out.

Line 3: The return value of the mocked out function. You can also throw exceptions, return back one of the arguments unmodified, or even call another callback function. The code for that is $this->throwException(new Exception()), $this->returnArgument($ArgumentNumber) and $this->returnCallback($callbackMethod) respectively. With the callback, all the parameters you pass to the mock will be passed to the callback.

Anyways, back to our example, I ran the tests and we see at our 0 boundary that we get behavior that we were not expecting and our tests fail.

1
PHPUnit_Framework_Error_Warning : Division by zero

Let’s fix that up.

1
2
3
4
5
6
7
8
9
10
class Calculator {
public function getNumberFromUserInput() {
// complicated function to get number from user input
}
 
public function divideBy($num2) {
if ($num2 == 0) return NAN;
return $this->getNumberFromUserInput()/$num2;
}
}

A few more failures…

PHPUnit_Framework_ExpectationFailedException : Expectation failed for method name is equal to  when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.

And finally…

1
2
3
4
5
6
7
public function testDivideByZero() {
$calcMock=$this->getMock('\Calculator',array('getNumberFromUserInput'));
$calcMock->expects($this->never())
->method('getNumberFromUserInput')
->will($this->returnValue(10));
$this->assertEquals(NAN, $calcMock->divideBy(0));
}

There are many options that you can apply to expects. Refer to this table: http://www.phpunit.de/manual/3.0/en/mock-objects.html#mock-objects.tables.matchers Oddly I could not find the same documentation on the current version of phpunit.

Lastly, if our calculator outputted data via a function, we can still test the output by mocking the output function.

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
// Calculator.php
class Calculator {
public function getNumberFromUserInput() {
// complicated function to get number from user input
}
 
public function printToScreen($value) {
// another complicated function
}
 
public function divideBy($num2) {
if ($num2 == 0) $this->printToScreen("NaN");
$this->printToScreen($this->getNumberFromUserInput()/$num2);
}
 
// CalculatorTest.php
..
public function testDivideByPositiveNumber() {
        $calcMock=$this->getMock('\Calculator',array('getNumberFromUserInput', 'printToScreen'));
        $calcMock->expects($this->once())
            ->method('getNumberFromUserInput')
            ->will($this->returnValue(10));
        $calcMock->expects($this->once())
            ->method('printToScreen')
            ->with($this->equalTo('5')); 
        $calcMock->divideBy(2);
    }
..

->with() will test the that the method is called with parameters passed. If your original function has multiple parameters, just add them in as multiple arguments to with(). ->with($this->equalTo($param1), $this->anything(),$this->equalTo($param3)). You can use any of the constraints that phpunit supports – http://www.phpunit.de/manual/3.2/en/api.html#api.assert.tables.constraints

You can git clone this full example on my github gist – https://gist.github.com/4558701

So with that said, test your code! With mocking, it makes it dead simple to test things. There should be no excuse to having tests on all the aspects of your code.