Friday, November 16, 2012

How to test a Zend Framework 2 Application

A Zend Framework 2 application has a couple of test suites. First of all, it contains Zend Framework 2 including its test suite. Then you have also your own module (or modules) which can contain its own test suites. Last but not least, you could have additional thirdparty modules (f.e. DoctrineORMModule or DoctrineModule) and thirdparty libraries (f.e. Symfony\Console, Doctrine\ORM, Doctrine\Common), which contain its own test suites.
To run all test suites for your application, you need to install phpunit and it's dependencies, then you need to run something like this:
phpunit -c vendor/zendframework/zendframework/tests/phpunit.xml
phpunit -c vendor/doctrine/common/phpunit.xml
phpunit -c modules/Application/tests/phpunit.xml
...

Well, of course you can create a simple bash script, that does the job for you. But if you want to exclude disable modules, that won't work. You have to change the script each time, you disable or install a module.

The solution: Humus PHPUnit Module

Humus PHPUnit Module is a Module for Zend Framework 2 for unit testing. It is able to test all your zf2 modules and libraries at once.

The installation is quite easy:


And you DON'T have to have phpunit installed at all! HumusPHPUnitModule will install EHER/PHPUnit through composer and you have phpunit installed in your vendor directory.

Usage is easy, too:
./vendor/bin/humusphpunit

How it works:
You have two possibilities, to enable a test suite for the humus phpunit module.
1) Using HumusPHPUnitModule\ModuleManager\Feature\PHPUnitProviderInterface
<?php

namespace MyModule;

use HumusPHPUnitModule\ModuleManager\Feature\PHPUnitProviderInterface;

class Module implements PHPUnitProviderInterface
{
    public function getPHPUnitXmlPaths()
    {
        return array(
            dirname(dirname(__DIR__)) . '/tests/phpunit.xml'
        );
    }
}

You can implement the PHPUnitProviderInterface in your own modules and provide an array of phpunit.xml files. The PHPUnit Runner collects them and runs all of them. To run the tests for thirdparty modules, that are independent from HumusPHPUnitModule, you can either write your own module, that returns phpunit.xml files for every thirdparty module you have (this can be one module per thirdparty-lib, but also a single one returning an array including all phpunit.xml files for all your thirdparty modules. Another way is using the configuration file.

2) Using configuration
<?php
return array(
    'humus_phpunit_module' => array(
        'phpunit_runner' => array(
            'Doctrine\Common' => array(
                'vendor/doctrine/common/phpunit.xml.dist'
            )
        )
    )
);

Just put this in your application configuration and run the bin script (./vendor/bin/humusphpunit).
That's all, there is also a HumusPHPUnitSampleApp - This is a demo application to show the features of Humus PHPUnit Module. It tests a sample application module with the PHPUnitListener and the Doctrine\Common lib and Zend Framework 2 with configuration.

5 comments:

  1. Hi,

    Look my PR Zend\Test component : https://github.com/zendframework/zf2/pull/2794 to make easier the app tests :)

    ReplyDelete
  2. Hi Vincent,

    I noticed your PR already, bit didn't had the time to go deeper in it. But if it doesn't start several independent phpunit processes, it's not a substitution for my module.

    However thanks for your hint.

    ReplyDelete
  3. Update:
    @Vincent your PR for Zend\Test component looks very nice. This will be very useful together with HumusPHPUnitModule. There goals differ, so I will use both of them.

    ReplyDelete
  4. Why would you want to run tests for Doctrine or ZF? Unless you're contributing to those projects, it doesn't make much sense to me.

    ReplyDelete
    Replies
    1. You should only use this module, if you have multiple phpunit.xml files in your project and want to run all of them. It's not meant to test multiple third party libraries.
      Running tests for Doctrine and ZF2 is just mentioned here as a working example.

      Delete