I am a 23-year-old living in Blacksburg, Virginia (Virginia Tech). I have been working with the web since I learned HTML in 7th grade and have been having a lot of fun with it ever since. I work for a local design and development company called New City Media as a PHP programmer and database developer. My work-load consists of mainly writing PHP code, designing database tables in MySQL 4/5 or MSSQL 2005, general tech support for our hosting, DNS, and database servers and also the occasional tech support call for a client. Mike is a DZone MVB and is not an employee of DZone and has posted 16 posts at DZone. You can read more from them at their website. View Full User Profile

CakePHP - Web Test Cases with SimpleTest

09.06.2011
| 5410 views |
  • submit to reddit

I'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.

CakePHP

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:

<?php  
class categoriesWebTestCase extends CakeWebTestCase  
{  
    function testcategoriesviewCase()   
    {  
        $this->get("http://example.com/categories/add");  
        $this->assertResponse(200);  
    }  
}  
?> 

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:

    <?php  
    class categoriesWebTestCase extends CakeWebTestCase  
    {  
        function testcategoriesviewCase()   
        {  
            $this->get("http://example.org/categories/");  
            $this->assertResponse(200);  
            $this->assertNoText("Error:");  
        }  
    }  
    ?>  

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:

    <?php  
    class categoriesWebTestCase extends CakeWebTestCase  
    {  
        function testcategoriesadmin_addCase()   
        {  
            $this->get("http://example.org/admin/categories/add");  
            $this->assertText("Login");  
            // Fill out the login form  
            $this->setField("data[User][username]", "admin");  
            $this->setField("data[User][password]", "password");  
            // Click Submit  
            $this->clickSubmit("Login");  
            // Now we should be in the admin area  
            $this->assertResponse(200);  
            $this->assertNoText("Error:");  
        }  
    }  
    ?>  

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:
    <?php  
    class categoriesWebTestCase extends CakeWebTestCase  
    {  
        function testcategoriesindexCase()   
        {  
            // First, open the index/list page  
            $this->get("http://example.com/categories/");  
            // Turn on OB to prevent printing data  
            ob_start();  
            $this->showSource();  
            $contents = ob_get_contents();  
            ob_end_clean();  
            // Grab any available ID's from links with the label Edit  
            $pattern = "/<a href=.*?([0-9]+)">Edit<\/a>/";  
            preg_match($pattern, $contents, $match);  
            if (!emptyempty($match[1])) {  
                // Restart the browser, and open the edit page  
                $this->restart();  
                $this->get("http://example.com/categories/edit/".$match[1]."");  
                $this->assertResponse(200);  
            }  
        }  
    }  
    ?>  

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.






Published at DZone with permission of Mike Bernat, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: