Zend Framework 1 and PHPUnit: Controller tests

This is a example of PHPUnit testing for Zend Framework 1. I haven’t used this framework for a while but do recall the struggle to achieve automated testing on routes. So, if anyone reading this is still stuck on this old framework I hope this might be of some help 🙂

Bootstrap file

<?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'));

Overriding TestCase dispatch method

You get many errors when running tests, it doesn’t help that Zend_Test_PHPUnit_ControllerTestCase::dispatch suppresses error messages. Anyway, the first thing I recommend is to just override the dispatch method in your test – all we are doing really is changing one line:

public function dispatch($url = null)
{
// redirector should not exit
$redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$redirector->setExit(false);

// json helper should not exit
$json = Zend_Controller_Action_HelperBroker::getStaticHelper('json');
$json->suppressExit = true;

$request = $this->getRequest();
if (null !== $url) {
$request->setRequestUri($url);
}
$request->setPathInfo(null);

$controller = $this->getFrontController();
$this->frontController
->setRequest($request)
->setResponse($this->getResponse())
->throwExceptions(true) // returnResponse(false);

if ($this->bootstrap instanceof Zend_Application) {
$this->bootstrap->run();
} else {
$this->frontController->dispatch();
}
}

Mocking library objects

Depending on the type of test you may want to mock more objects that others. For example, if running strictly unit tests on the controllers, you may mock authentication objects (e.g. Zend_Auth), models, mailers etc.

For my tests I prefer to create integrated tests where my tests are actually writing to a test database. Although my tests run slower, I don’t have to mock all my models, and their method calls. I tried this before but I found it too brittle. When I made a change to how I fetched the data from the model (e.g. add sort by column) then often my test would break and I’d have to spend time to get them to pass again. How thoughtlessly you wish to test your controller is up to you, but I’ve found that testing this way works well for me.

In order to mock objects in the application when testing, you need to be able to swap them out. The simplest form of this is to load objects into Zend_Registry during Bootstrap. In this example, I’ve replaced Zend_Auth:

function initRegistry()
{
Zend_Registry::set('mail', new Zend_Mail());
.
.
.
}

Mailer is a good example as we don’t want to be sending out emails during our tests.

Then in your tests, typically in the setUp method but certainly before dispatch() is run, you can replace the “mail” entry with a mock object of that class:

// get the mock auth object, and update the registry
$this->mailMock = $this->getMockBuilder('Zend_Mail')
->disableOriginalConstructor()
->getMock();

$this->mailMock
->method('send')
->willReturn(true);

Zend_Registry::set('mail', $this->mailMock);