Testing CakePHP Controllers the hard way

By now you already know or should know about CakeTestCase::testAction() and the wondrous things it can do. However, testAction has a few shortcomings. It can’t handle redirects, it doesn’t let you use the power of Mocks, and its impossible to make assertions on object state changes. Sometimes you need to do things the hard way, stick your fingers in the mud and work it out. However, knowing how to test a controller the old fashioned way takes a good knowledge of CakePHP.

Doing things the hard way

Controllers require a number of callbacks to be called before they are workable objects. So lets go, we’ll start with the venerable PostsController, and make a nice test for it.

So we start off with a basic test class, important things to notice are the fixtures array, and the test class. I’ve included all the fixtures that are related to the models my controller is going to use. This is important, as you will get tons of table errors until they are all setup.

You may have noticed that I created a subclass of the test subject, this lets me do a few things. First I can test functions that call redirect(), as they no longer redirect. I can also call methods that use $this->_stop() as they no longer halt script execution. Furthermore, I override Controller::render() so I can test actions that use render() without having to deal with piles of HTML. I personally don’t do many tests that assert the html of my views because I find it takes too much time and is tedious. Lastly, I set $autoRender to false just in case.

We then build the instance and call some basic callbacks, much like Daniel blogged about . At this point we have a controller instance and all the components and models built. We are now ready to start doing some testing.

Testing a controller method

Testing a controller method is just like testing any other method. Often there is a bit more setup involved as controllers are require more inputs by nature. However, it is all achievable in the test suite. So we are going to do a test of our admin_edit method. This admin_edit is straight out of bake, so you should know what it looks like. Furthermore, I can show how you can test methods that involve components like Auth.

At this point I’ve created the inputs I need for my controller action. I’ve got a session, and some test data. I’ve provided enough information in the session that AuthComponent will let me by and edit my records. However, many would say that you should bypass Auth entirely in your unit testing and just focus on the subject method. But being thorough never hurt.

I’ve now simulated most of a request in CakePHP. It is important to fire the callbacks in the correct order. Just remember that beforeFilter happens before Component::startup(), and Component::beforeRender() happens after you call your controller action.

Making assertions

When I test controllers I usually make assertions on the viewVars that are set and any records that are modified / deleted. I don’t like making assertions on the contents of $this->Session->setFlash() as I find these messages change often which can lead to broken tests, which leads to frowns. Continuing from before:

So there you go a nice simple test for a controller, with redirects and session flashes. Since we are testing with the real session we should do the following to ensure there is no bleed through between tests.

By destroying the session we ensure that we have a clean slate on each test method. So that’s it really testing controllers really isn’t as hard as it may seem. There are some additional tricks that can be done with Mocks but that is another article all together.

In startTest(), should
$this->Posts->Component->initialize();
be
$this->Posts->Component->initialize($this->Posts); ?

Thanks

pragnatek on 12/18/08

pragnatek: You are correct, fixed it. Guess that’s what happens when you start writing at 11pm.

mark story on 12/18/08

Hi.. i’m newbie. How to run all of test things above? using CLI ? can u tell me how to run those tests?

Thx :)

polutan on 12/23/08

polutan: You need to setup simpleTest. Once simpletest is setup you can run unit tests from either the CLI or a web browser. Perhaps reading the section of the book related to getting unit testing setup would help there.

On that very specific line of the auth.php we have:
$action = strtolower($controller->params[‘action’]);

I think the problem is obvious, ‘action’ was not set. But I wonder how should I go about fixing this? I thought this should be set automatically by cake?

raine on 12/27/09

Had the same problem as raine. I think it’s because we’re not using the Router so the params don’t get set for the url being ‘called’. At this point it gets complicated for those that have multiple admin sections with different prefixes and tests for them in the beforeFilter() of the controller.

Is there any way of calling urls using the test controller? maybe ‘/test_posts/index’ with testAction? (it doesn’t work with your setup since it’s not in the controllers folder… but putting it there would be a nice mess, mixing testing and real code…)

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.