UnitTesting in PHP with SimpleTest in Zend Framework (the simple and fast way)

Post Pic

In this post, I will show you why it is important to use Unit Testing, how to use it and how to avoid writing the same code 2 times. Also, i will show you how to do a basic Test Driven Development with SimpleTest and Zend Framework 1.10. Also, this tutorial will show you how to directly link your tests to the code of your application, without duplicating content. At the end, you will be able to extend the tests to run on any code you write.

Until the present, I was of the same opinion as other developers that Unit Testing in PHP is just way too much time consuming and complicated to do. So I managed all the coding and testing myself and fixed bugs as I cached up. I did try Unit testing in the past but just didn’t seem so appealing to use in all projects.

Why use Unit Testing

However, all that changed when I developed a very big application. From a points on, bugs started rolling out from everywhere. And while I was fixing them, almost every time I added a new feature, something else broke in the application. Before you start shouting that I just did a bad job at coding the whole application, I want to tell you that I DO agree with you from the start. But on a large scale application, you can easily start adding bugs without even knowing, and sometimes even while following best practices so this doesn’t change the fact that all those bugs could have been so easily spotted while developing (fixing bugs while writing the code is always MUCH faster then fixing them after a release). From that moment on I asked myself if I should use Unit Testing in the future. So I started reading about SimpleTest and PHPUnit .

In not to many words, I choose to use Unit Testing from now on because of the simple common myth that I realized is fake: You spend much more time writing in a TTD way than you would do otherwise. This myth assumes that, if you write tests for your application, you will ultimately write the code 2 times thus you spend 2 x more time coding. This is NOT true. You don’t write the code 2 times and you don’t spend 2 x more time writing the tests + application. Here is why.

You can run tests with the actual application files, and just validate the outcome. Once the test is passed, you already have written your application test file. All you have to do to achieve this is to make sure your application files use a Test Environment with fake data you can control. This changes everything since, once you have written the test and it passes, you are done with that feature. No more things to do, no more coding. You are completely done!

Also, I choose SimpleTest because it has a web based interface and you only need to copy paste its core files in order to install it (and of course because I was unable to make PHPUnit work with WAMP).

Disclaimer: I have used SimpleTest before for 1 day and i liked it. I am an amateur in terms of Unit Testing, so i would appreciate any tips or feedback you may leave in the comments below

Setting up SimpleTest

In order to write tests you will need 2 things: a working ZF project and the SimpleTest library. Once you have downloaded SimpleTest, move it in your public root directory of your ZF project like this:

Inside the simpletest directory, you should have the contents of the SimpleTest library.

Once you have done that, create a directory named tests in the same place as the SimpleTest library. Inside of it, create two files named Bootstrap.php and Index.php. Also create 1 directory named “Models” like this:

Now, go to the index.php file and add the following piece of code

< h1 >Tests to run
< br />
< h3 >Models
< ul >
< ? php
$dirHandle = opendir('Models/');

while (false !== ($file = readdir($dirHandle))) {
    if ($file != '.' && $file != '..')
    {
    	echo '< li >< a href="Models/'.$file.'">'. $file . '< /a >< /li >';
    }
}
?>

This basically will just show us when navigating to the tests directory, each individual test case we have coded.

Now if you go view you project in your browser and navigate to the tests directory (ex: http://localhost/myZfproject/tests/) you should see an almost empty file (except for a h1 and h2)

Setting up a Bootstrap File

In your bootstrap.php file (this file is different then your ZF project bootstrap file) add the following lines of code:

< ?php
ini_set('display_errors',1);
error_reporting(E_ALL ^ E_DEPRECATED);

define('DS',DIRECTORY_SEPARATOR);
define('LIBRARY_PATH',realpath(dirname(__FILE__)
	. DS . '..'
	. DS . '..'
	. DS . '..'
	. DS . 'library'
	. DS . '1.10')
);
define('APPLICATION_PATH',realpath(dirname(__FILE__)
	. DS . '..'
	. DS . '..'
	. DS . '..'
	. DS . 'MyProj' )
);
define('APPLICATION_ENV','testing');

$paths = array(
	LIBRARY_PATH,
	APP_LIBRARY_PATH,
	get_include_path()
);

set_include_path(implode(PATH_SEPARATOR, $paths));

/** Zend_Loader */
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();

// Custom functions to help development
require_once (
	dirname(dirname(APPLICATION_PATH))
	. DS . 'configs'
	. DS . 'MyProj'
	. DS . 'db.php' );

$config = new Zend_Config_Ini(
	dirname(dirname(APPLICATION_PATH))
	. DS . 'configs'
	. DS . 'MyProj'
	. DS . 'application.ini',
	APPLICATION_ENV );

$dbAdapter = Zend_Db::factory($config->resources->db);
Zend_Db_Table_Abstract::setDefaultAdapter($dbAdapter);

//Tests start here
require_once('../../simpletest/autorun.php');

Notice that I set error reporting NOT to show me deprecated errors (since SimpleTest is using some old functions that PHP considers deprecated).
In the bootstrap file we have just configured where the Zend Libary is located, where our application is located and what environment to use for testing.

We also loaded the autoloader since we want Zend to load classes for us automatically. We also setup how to connect to the database (based on the environment above selected).

The last thing I did was that I loaded the autorun library of SimpleTest, which allows me to run the actual test cases I will write.

Nothing fancy so far, but lets see the next step…

Writing your first test

I’ll assume you have a model named User.php . Based on this, create a file named User.php in your tests/Models directory, with the following contents

< ?php
require_once '../Bootstrap.php';
require_once '/var/www/applications/MyProj/models/Db/User.php';

class TestDefault_Model_Db_User extends UnitTestCase
{
	public function Test_getUserId()
	{
		$user = new Default_Model_Db_User();
		$this->assertEqual($user->getUserId('andrei'), 5);

//We know that the user with the username 'andrei' has the id 5
//We know this because the TESTING database contains this row
	}
}
?>

With this simple piece of code, all I’ve done is to include the bootstrap file that assures the test will run and included the actual model i have in the ZF Project.

Then, I just created a test class (it is mandatory it starts with Test) and I have ran a simple assertion to check that the method getUserId will return the correct id based on an username. Simple right?

Now, if I want to add a new feature to the getUserId method, I just edit my application model and run the test again. If it fails, its clear I added a feature with at least one bug that I need to fix.

Running your first test

To run a test, simply call your test case file from the browser and watch the awesomeness happen on the screen . (ex: http://localhost/myZfProject/tests/Models/User.php )

From here to…

Of course, now you can add more test cases (or even test suites) and also extend the functionality to basically test anything you want. Yes, even controllers, plugins, helpers and so on and forth! All you have to do is make sure you develop your code in TTD manner and make sure you don’t use global variables at all (or use them as little as possible) but that is another story, for another blog post. If you want to learn more about a specific issue in TTD, you can check this video out .

Let the tests begin!

blog comments powered by Disqus

Popular tags

Partner Blogs

Latest tweets


Get Adobe Flash playerPlugin by wpburn.com wordpress themes
Web Analytics