As you may or may not know CakePHP is transitioning to PHPUnit and with this transition comes a totally new Mock object interface/implementation. After porting some intensive mock object tests, I thought I would share what I’ve learned about the differences and similarities between the mocks in SimpleTest you may be familiar with.
Things that are the same
The basic premise of mocks is exactly the same between PHPUnit and SimpleTest. Mocks are used to stub out object method calls and used to assert that specific methods are called. Both frameworks use code generation to create subclass definitions and
eval() to make the usable mocks. Both frameworks allow you to create critic and actor mocks. That is about where the similarities end though, method calls to mocks can often be translated fairly easily and there are some example regular expressions available in the CakePHP lighthouse wiki.
A different way to the same ends
While the basic goals of both Mock object implementations is the same the means by which you get there are very different. With SimpleTest you would statically call
Mock::generatePartial() to create a mock subclass and then manually construct it. The SimpleTest has a big drawback in that you lose your constructor when mocking. This was fairly annoying and caused me grief in a number of occasions. Thankfully PHPUnit’s mocks allow you to use constructors like normal. Instead of calling a static class you create mocks by calling
getMock(). This method takes a pile of parameters and lacks really solid documentation. The parameters for
getMock() are as follows
$originalClassNameThe classname you want to get a mock object of
$methodsAn array of methods you wish to mock out. These can either be methods in the class, or new methods. Leaving this as an empty array will mock all the methods.
$argumentsArguments for the constructor of the created object.
$mockClassNameThe classname you want the mock object to have. If left blank one will be generated
$callOriginalConstructorSet to false to have the original constructor not called.
$callOriginalCloneSet to false to have the mocker replace
$callAutoloadUse autoloading to find the class to load.
The next big difference is how you use mocks. Unlike SimpleTest’s mocks, PHPUnit’s mocks have a fluent interface. This can make for easier to grok code in some cases. Instead of
- $mock = new MockThing();
- $mock->expectAt(0, 'send', array('one', 'two'));
- $mock->setReturnValueAt(0, 'send', true);
You would do something like
- $mock = $this->getMock('Thing');
- ->with('one', 'two')
PHPUnit splits mocks into a few separate parts. First in the method chain is the
expects(), which accepts a matcher as its parameter. The built in ‘matchers’ are
once()Will fail if the method is called more than once, or less than once.
never()Will fail if the method is called ever.
any()Will always match
at($index)Will match at call $index. A very important and possibly irritating difference between the SimpleTest implementation is the index increments each time a mock method is called, not just when the indicated method is called.
exactly($times)Will only pass if the method is called $times. I find this one doesn’t work well with
atLeastOnce()Will pass if the method is called more than once.
Usually, the next thing in the chain is a
method($methodName) call, which specifies which method the expectation is for. Following
method() is usually one or both of
will(). Parameter expectations are supplied as parameters to
with(). You can either supply the literal values you are expecting, or one of the many constraint objects PHPUnit has. Return values for mocked methods are set with
will(). Will accepts a ‘stub’ as its parameter, and there are several built in stub types.
returnValue($value)Return a literal value, like a string or an array or a boolean.
throwException($exception)Make the method throw an exception.
returnArgument($index)Return the argument at $index
returnCallback()Allows you to return ‘callback type’ results. For example in PHP5.3 you can return anonymous functions.
onConsecutiveCalls()Allows you to setup a return value ‘script’. This allows you to setup a number of return values that will be returned in the sequence they are provided. This stub works best with either
atLeastOnce()in my experience.
Overall, I found the fluent interface took only a short while to get used to. The difference in how
at() works threw me off for a bit, but once I figured it out it made sense. I find the fluent interface quite readable and offers some features that SimpleTest does not. For me, the most interesting of these was
$this->onConsecutiveCalls(), which allows you to ‘script’ the return value of a method each time its called. I found this really helpful when writing tests for shell classes where I wanted to script the return values of
While fairly different from the mock objects in SimpleTest, I find that the mocks in PHPUnit offer a few more features and one more good thing that the transition to PHPUnit affords.