.. _user-guide.unit-testing.rst:
############
Unit Testing
############
A solid unit test suite is essential for ongoing development in large
projects, especially those with many people involved. Going back and
manually testing every individual component of an application after
every change is impractical. Your unit tests will help alleviate that
by automatically testing your application's components and alerting
you when something is not working the same way it was when you wrote
your tests.
The Zend Framework 2 API uses `PHPUnit `_, and so
does this tutorial application. A detailed explanation of unit testing
is beyond the scope of this tutorial, so we will only provide sample
tests for the components in the pages that follow. This tutorial assumes
that you already have PHPUnit installed.
Setting up the tests directory
------------------------------
Start by creating a directory called ``test`` in ``zf2-tutorial\module\Application`` with
the following subdirectories:
.. code-block:: text
zf2-tutorial/
/module
/Application
/test
/ApplicationTest
/Controller
The structure of the ``test`` directory matches exactly with that of the
module's source files, and it will allow you to keep your tests
well-organized and easy to find.
Bootstrapping your tests
------------------------
Next, create a file called ``phpunit.xml.dist`` under ``zf2-tutorial/module/Application/test``:
.. code-block:: xml
./ApplicationTest
A file called ``Bootstrap.php``, also under ``zf-tutorial/module/Application/test``:
This is a Bootstrap written by Evan Coury which can just be dropped in, only the namespace needs changing.
.. code-block:: php
array(
'module_paths' => explode(PATH_SEPARATOR, $zf2ModulePaths),
),
);
$config = ArrayUtils::merge($baseConfig, $testConfig);
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
static::$config = $config;
}
public static function getServiceManager()
{
return static::$serviceManager;
}
public static function getConfig()
{
return static::$config;
}
protected static function initAutoloader()
{
$vendorPath = static::findParentPath('vendor');
if (is_readable($vendorPath . '/autoload.php')) {
$loader = include $vendorPath . '/autoload.php';
} else {
$zf2Path = getenv('ZF2_PATH') ?: (defined('ZF2_PATH') ? ZF2_PATH : (is_dir($vendorPath . '/ZF2/library') ? $vendorPath . '/ZF2/library' : false));
if (!$zf2Path) {
throw new RuntimeException('Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.');
}
include $zf2Path . '/Zend/Loader/AutoloaderFactory.php';
}
AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true,
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/' . __NAMESPACE__,
),
),
));
}
protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) return false;
$previousDir = $dir;
}
return $dir . '/' . $path;
}
}
Bootstrap::init();
And a file called TestConfig.php.dist
.. code-block:: php
array(
'Application',
),
'module_listener_options' => array(
'config_glob_paths' => array(
'../../../config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'module',
'vendor',
),
),
);
This is basically the same as config/application.config.php but we define only the modules which are required for this test
Your first Controller test
--------------------------
Next, create ``IndexControllerTest.php`` under
``zf-tutorial/module/Application/test/ApplicationTest/Controller`` with
the following contents:
.. code-block:: php
controller = new IndexController();
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => 'index'));
$this->event = new MvcEvent();
$config = $serviceManager->get('Config');
$routerConfig = isset($config['router']) ? $config['router'] : array();
$router = HttpRouter::factory($routerConfig);
$this->event->setRouter($router);
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setServiceLocator($serviceManager);
}
}
Here, we expand a bit on the setup in Tom Oram's
`Unit Testing a ZF 2 Controller `_
blog entry by initializing our application in the ``setUp()`` method and
setting the ``EventManager`` and ``ServiceLocator`` directly on the controller.
This isn't important right now, but we'll need it later on when writing more
advanced tests.
Now, add the following function to the ``IndexControllerTest`` class:
.. code-block:: php
public function testIndexActionCanBeAccessed()
{
$this->routeMatch->setParam('action', 'index');
$result = $this->controller->dispatch($this->request);
$response = $this->controller->getResponse();
$this->assertEquals(200, $response->getStatusCode());
}
The test is verifying that the homepage responds with HTTP status code 200 and
that the controller's return value is an instance of ``Zend\View\Model\ViewModel``.
Testing
-----------
Finally, ``cd`` to ``zf-tutorial/module/Application/test/`` and run ``phpunit``. If you see something like
this, then your application is ready for more tests!
.. code-block:: text
PHPUnit 3.5.15 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 5.75Mb
OK (1 test, 2 assertions)