2008.12.10

CakePHPI've been having a blast with CakePHP in the last month or two, and I thought I would share some of the things I've learned. So, in the next couple weeks I'll be documenting some questions/frustrations I've encountered and my solutions.

First off, the community and developers have been absolutely outstanding in their support. I've found when google fails to give me an answer to my questions, the cake irc channel or google discussion group will explain the solution precisly and informativly.

The Problem

Anyway, back to the real story. I have fallen in love with CakePHP's integration of the SimpleTest libraries. With the type of work that I normally do, unit-testing is hard to utilize successfully. That is to say, most of the applications I work on have very straight-forward components and not a lot of complex functions/methods. I would only be testing whether or not they worked at all, rather than if they worked in a wide-array of situations.

SimpleTestFor example, unit-testing a simple news list and detail page is probably overkill. Sure, you can test your classes by simple instantiating them but that only goes so far. My new method involves using SimpleTest's Scriptable Browser to actually crawl webpages and ensure that the proper data is being displayed. That way, I can catch all my php errors, including notices and warnings, ensure that the proper headers are being sent, and assert that certain text is appearing on the page. Unit-testing will rarely catch a poorly coded method that throws a PHP notice whereas the Scriptable Browser will.

So, now that everyone knows that SimpleTest's Scriptable Browser is the greatest thing since xDebug, what's the best way to implement it?

Solutions

Here is a basic Web Test Case that I use in Cake:

Explination: This is about as simple as it gets. Here, I have a short class that extends CakeWebTestCase - this is required. Each method of a test class is considered to be a test-case, and it can have many tests. In this case, I only have one test-case with one test. The get() method of SimpleTest's browser requests the page just as any browser would do. I then assert that the response header was 200 or OK. The assertResponse() method is considered to be the test. This ensures that the page loaded correctly and didn't throw any others headers such as a 404 - not found.

 

Let's take it up a notch and make sure the page doesn't have any common PHP errors:

Explination: Same kind of thing as before, with an additional test. It may be obvious, but the assertNoText() method will ensure that the page does not contain any of these strings. This is perfect for catching PHP errors, cake errors, and anything else you might find undesireable on a page.

It should be said that when a site is in 'Production-Mode' that these kinds of errors should be turned off, but during development this added test can be extremely useful.

 

What about admin, or password-protected, pages? SimpleTest has you covered:

Explination: Again, SimpleTest's methods are very smartly named so that one can deduce their function without too much trouble. In this case, we are trying to open a page in a password-protected area. The first thing we do is open the page and assert that Cake properly redirected us to the login page. I accomplish this by checking to make sure that 'Login' text is on the page. The next couple lines actually fill in the login form fields with an authorized username and password. clickSubmit() does exactly why you think it does, it 'clicks' an input of type submit with the value 'Login'. At this point, our original page should load as Cake normally redirects you back to where you wanted to end up originally. I wrap up by performing a few more tests to make sure there are no error messages. It's that easy!

 

But what about edit and view pages? You need an ID in the url to properly access the page. How can you be sure that your tests will always pull an existing ID? This is where I had the most trouble with SimpleTest but I have come up with an effective solution:

Explination: A couple of new things here. In order to open edit/view pages, I need to have the primary key. The only way to ensure that the ID exists in the database is to scrape the screen for actual keys. So how do you get the contents of a page? The showSource() method will print the returned content to the screen. To prevent this, I turn on output-buffering, grab the contents, and empty the buffer. Once I have the contents of the index or list page handy, I can find any links to add/edit pages with the corrosponding ID numbers. After using a regular expression to match the specific id number, I can open the edit page. Before that though, I restart the browser to clear out anything unusual that may have come through and ensure that the page returns the proper header.

There may be a better way to do this (there usually is), but this has worked well for me so far.

Execute the Tests

The only thing I have neglected to mention is how to actually run the tests. That could be important...

From The Bakery

First of all, you'll need to enable the test suite for your CakePHP 1.2 installation. After CakePHP is succesfully installed and configured, get the latest release of SimpleTest from its website, and uncompress it in either your cake/vendors or your app/vendors directory. You should have now a vendors/simpletest directory with all SimpleTest files and folders inside.

Make sure that you at least have a DEBUG level of 1 in your app/config/core.php file. Test your installation by running any of CakePHP core tests, pointing your browser to http://www.example.com/test.php.

Additionally, you may have to change the include path to the cake core inside test.php. Once you have it open in the browser, you can run your application's tests.

Conclusion

SimpleTest has a ton of functionality, you can learn more at their website http://simpletest.org/. I found their documentation page and more specifically the scriptable browser section were extremely helpful.

Let me know if anyone finds this useful.

Get my RSS Feed!

Comments

John on (1.5.2009 10:00 pm) says

FYI, I could not get this line to fail:

$this->assertNoText(array("Notice:", "Warning:"));

Instead I had to use:

$this->assertNoText("Notice:");
$this->assertNoText("Warning:");

-John

 

Mike Bernat on (1.6.2009 1:50 pm) says

Good catch John. I could have sworn in an earlier version of Simple Test that you could pass it an array of strings. Thanks!

 

Dooltaz on (1.8.2009 5:03 pm) says

I just learned git, and now I'm getting into unit testing. This is a great tip! I didn't see a way to bake unit tests for views, so this is great. Thanks!

 

Mohammed on (3.18.2010 3:17 pm) says

Hi Mike,

I don't know if I understand fully  but I found that after filling in a login form in a password protected page using the code presented, if I write $this->showSource(); after: "//Now we should be in the admin area", it prints the login page code instead indicating that it didn't login. I'm new to using the test suite so I don't know if this is normal or expected. 

Thanks,
Mohammed

 

Aung on (5.21.2010 6:46 am) says

This article is precise and exact to the point. *two thumbs up*. :) .

However, I am now working on one project and our API cannot be called directly. We have to use 
* Method: POST
* Header Name: Content-Type
* Header Value: application/x-www-form-urlencoded
* Body: params={"username":"aung","password":"pass"}.

I can solve the post by using post method. But i don't have any clue to pass the header name, value and params. :( ... Please would u kind enough to help me :) ... thank in advance.

 
* Name
* Email (Will not be displayed)
Website