Testing CakePHP controllers - Mock Objects edition

I recently wrote an article about testing CakePHP controllers the hard way where I covered testing controllers by running their methods manually. I hinted at some additional tricks that could be performed by using Mock Objects. Today I’m going to spill the beans on Mocks, and how I use them when testing my Controllers.

What is a Mock Object

First figuring out what Mock objects are and are not is important. The wikipedia says

In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A computer programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts.Wikipedia

In unit testing we use mocks both as a way to isolate our unit (the object being tested) from the world and to allow us to create unexpected situations. Ever wonder how your application would react if a random value was injected, or wonder how you can easily trigger that exception that relies on FooBarComponent returning false which it only does if the file system is corrupted? Well these are both situations that can be simulated with Mock Objects. Mock objects can also take an active role in Unit testing and make contribute additional assertions to your tests, in the form of expectations. Mocks are not the solution to all your testing woes, nor will they make a good cup of coffee, but moving on.

Where can I get one of these fabulous devices?

Well since we are working in the context of a CakePHP unit test and therefore using Simple Test. We get Mock objects from the Mock class. We generate Mock objects from existing classes, and due to the magic of Reflection and eval() a Mock object class is generated. The simplest example would be:

This will generate a class called MockEmailComponent. This MockEmailComponent will have all the same methods as our real EmailComponent. One big difference between the real object and the mock is that all of the mock methods do not work. They all return null and take any number of arguments. But hold on a second, if its methods don’t have returns what good are they? Well lots, because you can set the return value or return reference value of all its methods.

This will set the return value of send() to true. However, it will not send an email, which is the nice part. Because getting emails from your test suite is never fun. Also it allows the test to run on machines that don’t even have e-mail servers, like a development box. Using a Mock to test email being sent also allows you to test what happens when your email server is down or other difficult to simulate situtations. Generating a Mock object also allows you to append extra methods onto your objects, such as those your are going to build but haven’t. This would look a little something like

We’ve now added the methods slice() and dice() to our MockEmailComponent. In addition to complete mocks, we can build partial Mocks. A partial mock is just what it sounds like. It has only a few of its methods mocked. The rest stay as is in the declared classe(s). This is really handy for Objects that use only a few methods to write to a resource. An example of this would be your controllers. In my previous article I used a subclass to dummy out the render and redirect methods. However, we could also do this with partial mock objects.

In our tests we can now use Mock expectations to assert that our redirects and render calls are occurring properly.

Makings expectations with Mock Objects

Mocks can be used to feed your application values as seen above. Furthermore, Mock Objects can be used to introspect on your Unit and ensure that it is properly delegating / calling methods on its inner objects. Say for example we wanted to test the use of SessionComponent::setFlash(). Now, we could not mock it, and make an an assertion before and after the method has run to test that the value in the session was not set, and then is set. This will work just fine until we start adding lots of test methods that use the session. We could easily run into bleed-through between our tests, causing our tests to become dependent on the order in which they are run, or worse yet create broken tests. This is no good. Furthermore, using the real session will nuke any sessions we have with the box we are testing on. Using a Mock object for our Session solves all of these problems.

In the above we’ve not only set a return value for our SessionComponent’s read() method when passed the argument of Auth.User.id but we’ve also made an expectation that setFlash() will be called exactly once. If it is called twice or never this assertion will fail, and we will get the red bar of doom. Notice that I didn’t make an assertion on the value being passed to setFlash(). You totally can expect certain parameters to be passed to mock methods. However, I find setting assertions for the values being fed into methods like setFlash() can be subject to a lot of change. If we were to make assertions on these inputs, we would need to update our tests each time the message changes. I personally find I’m more interested that the method is called, giving the user feedback then what the exact contents of that feedback are.

There is so much that can be said about mock objects. However, this post is long enough for the time being. Be sure to check out the SimpleTest Documentation on Mock Objects for a complete reference on the API and more additional information about SimpleTest mock objects.

Comments

Thanks for this post. It’s very clear on how to use Mock objects and a very useful introduction to the simpletest chapter on the subject from a cakephp perspective.

Thanks for the great articles on Controller testing…they’ve been a huge help. I’m having trouble mocking up the controller objects themselves, though.

I’ve got a controller action that calls several functions elsewhere in the controller, and since I’m unit testing, I wanted to isolate the action being tested. So I wanted to do a partial mock of the controller object so that those functions wouldn’t be called. Here’s what I have:

Ryan W: You’ve stumbled upon one of the toughies with mocks in simple test. You have the right approach though. The problem comes from the Mock object itself. A partial mock overloads the __construct() of the mocked object but never calls parent::__construct() So your controller object is never properly built. There are a few ways around this. One is to define your own mock class and tell simple test to use that before you generate your partial mock. Another is to emulate what Controller::__construct() does and build Component and popluate $methods manually. Both are not fun, I’ve done the custom mock approach and it is tricky to get working.

mark story on 2/20/09

Sorry, but the cursive font is very hard to read….

Sans Serif on 3/24/09

This is great, Mark. I’ve been using JMock on a different project and missing that capability in my PHP work. This makes me happy!

I am running into the same thing that Ryan W was seeing. I am afraid I do not understand your comment Mark. Could anyone provide an example of how to get around this?

Jon R on 1/4/12

Nevermind! I do not know if it will work for all cases but I just literally copied the logic from Controller.__construct() into a controller base class and modified all references to ‘this’ to ‘this->controller’. ‘controller’ is a variable I created to hold a reference to the controller I am testing. When I create a controller test I have to set the variable in startTest() and then call parent::startTest() which holds all of the logic from Controller.__construct().

Recent Artwork

Links

Mark is a designer and web-developer, working with standards compliant HTML and CSS. He has been building websites since 2000. Currently he is employed at Freshbooks as a developer, and actively contributes to open source projects specifically CakePHP. He uses this site as a place to share what he has learned and made.