I haven't really done much unit testing but recently I have to mingle with it a little bit for this task that I was asked to do. I will walk through on how I set it up in ZF (Zend Framework) and share how I solve some problems I encountered.
I will assume you have already installed PHPUnit. If not
go here to read the install documentation.
As you may noticed every time you create a new controller in ZF using zend tool a separate test controller is created in /tests/application/controllers but before we can use PHPUnit we need to setup our bootstrap.
1.) Go to and edit /tests/phpunit.xml and add this:
<phpunit bootstrap="./application/bootstrap.php" colors="true">
<testsuite name="school">
<directory>./</directory>
</testsuite>
<filter>
<whitelist>
<directory suffix=".php">../library/</directory>
<directory suffix=".php">../application/</directory>
<exclude>
<directory suffix=".phtml">../application/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
The above will tell PHPUnit where to find your bootstrap and what type of files to look for. You can use this if you are running test from the test parent directory.
2.) Now edit the /tests/application/bootstrap.php file and add this:
<?php
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../../application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'testing'));
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));
require_once 'ControllerTestCase.php';
The part you want to take a note on the above script is:
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));
This is important because if you do not include the ZF library in your PHP's include path, PHPUnit will not be able to find your library without you explicitly specifying the location.
2.) Now let's create an abstract class for our test cases to extend from. Go to /tests/application and create a file called
ControllerTestCase.php and add:
<?php
require_once 'Zend/Application.php';
require_once 'Zend/Test/PHPUnit/ControllerTestCase.php';
abstract class ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
public function setUp()
{
$this->bootstrap = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
parent::setUp();
}
public function tearDown()
{
$this->resetRequest()->resetResponse();
$this->request->setPost(array());
$this->request->setQuery(array());
}
}
NOTE: The above class is the class that you will need to extend when writing the actual tests.
3.) Edit your main application bootstrap and add the following:
/application/bootstrap.php
protected function _initLogger()
{
$this->bootstrap('frontController');
if ($this->getEnvironment() == "testing") {
$writer = new Zend_Log_Writer_Stream(APPLICATION_PATH . "/../data/logs/app.log");
$filter = new Zend_Log_Filter_Priority(Zend_Log::CRIT);
}
// register the Zend_Application_Resource_Log plugin first if you havent done so
$this->registerPluginResource('log', array('writer' => $writer));
// make sure to bootstrap
$this->bootstrap('log');
// retrieve Zend_Log and add the filter
$this->_logger = $this->getResource('log')
->addFilter($filter);
}
Then edit your ErrorController.php and change/append the part in your error action where it says
"// Log exception, if logger available" and change it to the following. This is if you happen to be using the default Error Controller:
// Log exception, if logger available
$log = $this->getLog();
if ($log) {
$message = $this->view->message . PHP_EOL;
$message .= $errors->exception . PHP_EOL;
$message .= '---------------------------';
$log->crit($message);
}
The above script will attempt to log error messages in the
stream location that you set. This is so that if there is an error within your application itself unrelated to the test error such as "Application Error" or "Page Not Found Error", you will be able to debug the test. I realize that I need to do this because PHPUnit does not really report any information when you get these types of error. One example is if you
$this->dispatch('/notexist') to a non existing action you will get an error in PHPUnit saying:
Failed asserting last action used <"error"> was "notexist";
That error in PHPUnit will not say much to you to debug from but it will log something in app.log.
4.) Now we can write a sample test script. Edit
/tests/application/controller/IndexControllerTest.php if you have one and replace it to:
<?php
file_exists('../bootstrap.php') ? require_once '../bootstrap.php' : '';
require_once 'PHPUnit/Framework/TestCase.php';
class IndexControllerTest extends ControllerTestCase
{
public function testShouldCallStudentSearch()
{
$this->dispatch('/');
$this->assertAction('index');
$this->assertController('index');
$this->assertModule('MyModule');
}
}
One last thing to note here is the line:
file_exists('../bootstrap.php') ? require_once '../bootstrap.php' : ''; that I manually added. I did this so I can test the IndexController.php directly within the controller directory:
shell> cd test/application/controllers
shell> phpunit IndexControllerTest.php
Also notice that our class extends
ControllerTestCase instead.
If you want to test every test controller that you created, make sure to test from the
test directory:
shell> cd /test
shell> phpunit