15. Cookbook

15.1. Change the default namespace

At the execution beginning of a test class, atoum computes the name of the tested class. To do this, by default, it replaces in the class name the following regular expression #(?:^|\\\)tests?\\\units?\\#i by char \.

Thus, if the test class name is vendor\project\tests\units\foo, it will deduct in that the tested class named is vendor\project\foo. However, it may be necessary that the namespace of the test classes may not match this regular expression, and in this case, atoum then stops with the following error message:

> exception 'mageekguy\atoum\exceptions\runtime' with message 'Test class 'project\vendor\my\tests\foo' is not in a namespace which match pattern '#(?:^|\\)ests?\\unit?s\#i'' in /path/to/unit/tests/foo.php
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

We must therefore change the regular expression we used, this is possible in several ways. The easiest way is to applied the annotations @namespace to the test class in the following way :

<?php

namespace vendor\project\my\tests;

require_once __DIR__ . '/atoum.phar';

use mageekguy\atoum;

/**
 * @namespace \my\tests
 */
abstract class aClass extends atoum
{
   public function testBar()
   {
      /* ... */
   }
}

This method is quick and simple to implement, but it has the disadvantage of having to be repeated in each test class, which is not so maintainable if there is some change in their namespace. The alternative is to call the atoum\test::setTestNamespace() method in the constructor of the test class, in this way:

<?php

namespace vendor\project\my\tests;

require_once __DIR__ . '/atoum.phar';

use mageekguy\atoum;

abstract class aClass extends atoum
{
   public function __construct(score $score = null, locale $locale = null, adapter $adapter = null)
   {
      $this->setTestNamespace('\\my\\tests');

      parent::__construct($score, $locale, $adapter);
   }

   public function testBar()
   {
      /* ... */
   }
}

The atoum\test:setTestNamespace() method indeed accepts a single argument which must be the regular expression matches the namespace of your test class. And to not have to repeat the call to this method in each test class, just do it once and for all in an abstract class in the following manner:

<?php

namespace vendor\project\my\tests;

require_once __DIR__ . '/atoum.phar';

use mageekguy\atoum;

abstract class Test extends atoum
{
   public function __construct(score $score = null, locale $locale = null, adapter $adapter = null)
   {
       $this->setTestNamespace('\\my\\tests');

      parent::__construct($score, $locale, $adapter);
   }
}

Thus, you will only have to do derive your unit test classes from this abstract class:

<?php

namespace vendor\project\my\tests\modules;

require_once __DIR__ . '/atoum.phar';

use mageekguy\atoum;
use vendor\project\my\tests;

class aModule extends tests\Test
{
   public function testDoSomething()
   {
      /* ... */
   }
}

In case of unit tests namespace change, it is therefore necessary to change only the abstract class.

Moreover, it’s not mandatory to use a regular expression, either at the level of the @namespace annotation or the method atoum\test::setTestNamespace() a simple string can also works.

Indeed atoum by default use a regular expression so that the user can use a wide range of namespaces without the need to configure it at this level. This therefore allows it to accept for example, without any special configuration the following namespaces:

  • test\unit\
  • Test\Unit\
  • tests\units\
  • Tests\Units\
  • TEST\UNIT\

However, in general, the namespace used to test classes is fixed, and it’s not necessary to use a regular expression if the default isn’t suitable. In our case, it could be replaced with the string my\tests, for example through the @namespace annotation :

<?php

namespace vendor\project\my\tests;

require_once __DIR__ . '/atoum.phar';

use mageekguy\atoum;

/**
 * @namespace \my\tests\
 */
abstract class aClass extends atoum
{
   public function testBar()
   {
      /* ... */
   }
}

15.2. Test of a singleton

To test a method that always returns the same instance of an object, checks that two calls to the tested method are the same.

<?php
$this
    ->object(\Singleton::getInstance())
        ->isInstanceOf('Singleton')
        ->isIdenticalTo(\Singleton::getInstance())
;

15.3. Hook git

A good practice, when using a version control system, is to never add a non-functional code in repository, in order to retrieve a version clean and usable code at any time and any place the history of the deposit.

This implies, among other things, that the unit tests must pass in their entirety before the files created or modified are added to the repository, and as a result, the developer is supposed to run the unit tests before pushed its code to the repository.

However, in fact, it is very easy for the developer to omit this step and your repository may therefore contain, more or less imminent, code which does not respect the constraints imposed by unit tests.

Fortunately, version control software in general and in particular Git has a mechanism, known as the pre-commit hook name to automatically perform tasks when adding code in a repository.

The installation of a pre-commit hook is very simple and takes place in two stages.

15.3.1. Step 1: Creation of the script to run

When adding code to a repository, Git looks for the file .git/hook/pre-commit in the root of the repository and executes it if it exists and that it has the necessary rights.

To set up the hook, you must therefore create the .git/hook/pre-commit file and add the following code:

The code below assumes that your unit tests are in files with the extension .php and directories path contains / Tests/Units. If this is not the case, you will need to modify the script depending on your context.

Note

In the above example, the test files must include atoum for the hook works.

The tests are run very quickly with atoum, all unit tests can be run before each commit with a hook like this :

#!/bin/sh
./bin/atoum -d tests/

15.3.2. Step 2: Add execution rights

To be usable by Git, the file .git/hook/pre-commit must be executable by using the following command, executed in command line from the directory of your deposit:

$ chmod u+x `.git/hook/pre-commit`

From this moment on, unit tests contained in the directories with the path contains / Tests/Units will be launched automatically when you perform the command git commit, if files with the extension .php have been changed.

And if unfortunately a test does not pass, the files will not be added to the repository. You must then make the necessary adjustments, use the command git add on modified files and use again git commit.

15.4. Use in behat

The asserters from atoum are very easy to use outside your traditional unit tests. Just import the class mageekguyatoumasserter without forgetting to load the required classes (atoum provides an autoload class available in classes/autoloader.php). The following example illustrates this usage of asserter from atoumin your Behat steps.

15.4.1. Installation

Simply install atoum and Behat in your project via pear, git clone, zip... Here is an example with dependency manager Composer :

"require-dev": {
        "behat/behat": "2.4@stable",
        "atoum/atoum": "~2.5",
}

It is obviously mandatory to update your composer dependencies with the command :

$ php composer.phar update

15.4.2. Configuration

As mentioned in the introduction, just import the asserter classes from atoum and ensure that they are loaded. For Behat, configuration of asserters are done inside the class FeatureContext.php (located by default in your directory /root-of-project/features/bootstrap/).

<?php

use Behat\Behat\Context\ClosuredContextInterface,
    Behat\Behat\Context\TranslatedContextInterface,
    Behat\Behat\Context\BehatContext,
    Behat\Behat\Exception\PendingException,
    Behat\Behat\Context\Step;
use Behat\Gherkin\Node\PyStringNode,
    Behat\Gherkin\Node\TableNode;

use atoum\asserter; // <- atoum asserter

require_once __DIR__ . '/../../vendor/atoum/atoum/classes/autoloader.php'; // <- autoload

class FeatureContext extends BehatContext
{
    private $assert;

    public function __construct(array $parameters)
    {
        $this->assert = new asserter\generator();
    }
}

15.4.3. Usage

After these 2 particular trivial steps, your steps can be extended with the atoum asserters :

<?php

// ...

class FeatureContext extends BehatContext
{//...

    /**
     * @Then /^I should get a good response using my favorite "([^"]*)"$/
     */
    public function goodResponse($contentType)
    {
        $this->assert
            ->integer($response->getStatusCode())
                ->isIdenticalTo(200)
            ->string($response->getHeader('Content-Type'))
                ->isIdenticalTo($contentType);
    }
}

Once again, this is only an example specific to Behat but it remains valid for all needs of using the asserters of atoum outside the initial context.

15.5. Use with continous integration tools (CI)

15.5.1. Use inside Jenkins (or Hudson)

It’s very simple to the results of atoum to Jenkins (or Hudson) as xUnit results.

15.5.1.1. Step1: Add a xUnit report to the configuration of atoum

Like other coverage report, you can use specific report from the configuration.

15.5.1.1.1. If you don’t have a configuration file

If you don’t have a configuration file for atoum yet, we recommend that you extract the directory resource of atoum in that one of your choice by using the following command :

  • If you are using the Phar archive of atoum :
$ php atoum.phar --extractRessourcesTo /tmp/atoum-src
$ cp /tmp/atoum-src/resources/configurations/runner/xunit.php.dist /my/project/atoum.php
  • If you are using the sources of atoum :
$ cp /path/to/atoum/resources/configurations/runner/xunit.php.dist /my/project/.atoum.php

There is one last step, edit this file to set the path to the xUnit report where atoum will generate it. This file is ready to use, with him, you will keep the default report and gain a xUnit report for each launch of tests.

15.5.1.1.2. If you already have a configuration file

If you already have a configuration file, simply add the following lines:

<?php

//...

/*
 * Xunit report
 */
$xunit = new atoum\reports\asynchronous\xunit();
$runner->addReport($xunit);

/*
 * Xunit writer
 */
$writer = new atoum\writers\file('/path/to/the/report/atoum.xunit.xml');
$xunit->addWriter($writer);

15.5.1.2. Step 2: Test the configuration

To test this configuration, simply run atoum specifying the configuration file you want to use :

$ ./bin/atoum -d /path/to/the/unit/tests -c /path/to/the/configuration.php

Note

If you named your configuration file .atoum.php, it will be load automatically. The -c parameter is optional in this case. To let atoum load automatically the .atoum.php file, you will need to run test from the folder where this file resides or one of his childs.

At the end of the tests, you will have the xUnit report inside the folder specified in the configuration.

15.5.1.3. Step 3: Launching tests via Jenkins (or Hudson)

There are several possibilities depending on how you build your project :

  • If you use a script, simply add the previous command.
  • If you use a utility tool like phing or ant, simply add an exec task like :
<target name="unitTests">
  <exec executable="/usr/bin/php" failonerror="yes" failifexecutionfails="yes">
    <arg line="/path/to/atoum.phar -p /path/to/php -d /path/to/test/folder -c /path/to/atoumConfig.php" />
  </exec>
</target>

Notice the addition of -p /path/to/php that permit to atoum to know the path to the php binary to use to run the unit tests.

15.5.1.4. Step 4: Publish the report with Jenkins (or Hudson)

Simply enable the publication of report with JUnit or xUnit format of the plugin you are using, specifying the path to the file generated by atoum.

15.5.2. Use with Travis-CI

It’s simple to use atoum with a tool like Travis-CI. Indeed, all the steps are described in the official documentation : * Create your .travis.yml in your project; * Add it the next two lines:

before_script: wget http://downloads.atoum.org/nightly/atoum.phar
script: php atoum.phar

Here is an example file .travis.yml where the unit tests in the tests folder will be run.

language: php
php:
  - 5.4
  - 5.5
  - 5.6

before_script: wget http://downloads.atoum.org/nightly/atoum.phar
script: php atoum.phar -d tests/

15.6. Use with Phing

atoum test suite can be easily ran inside your phing configuration using the integrated phing/AtoumTask.php task. A valid build example can be found in the resources/phing/build.xml file.

You must register the custom task using the taskdef native phing task :

<taskdef name="atoum" classpath="vendor/atoum/atoum/resources/phing" classname="AtoumTask"/>

Then you can use it inside one of your buildfile target:

<target name="test">
  <atoum
    atoumautoloaderpath="vendor/atoum/atoum/classes/autoloader.php"
    phppath="/usr/bin/php"
    codecoverage="true"
    codecoveragereportpath="reports/html/"
    showcodecoverage="true"
    showmissingcodecoverage="true"
    maxchildren="5"
  >
    <fileset dir="tests/units/">
      <include name="**/*.php"/>
    </fileset>
  </atoum>
</target>

The paths given in these examples have been taken from a standard composer installation. All the possible parameters are defined below, you can change values or omit some to rely on defaults. There is three kind of parameters:

15.6.1. atoum configurations

  • bootstrap: Bootstrap file to be included before executing each test method
    • default: .bootstrap.atoum.php
  • atoumpharpath: If atoum is used as phar, path to the phar file
  • atoumautoloaderpath: Autoloader file before executing each test method
    • default: .autoloader.atoum.php
  • phppath: Path to php executable
  • maxchildren: Maximum number of sub-processus which will be run simultaneously

15.6.2. Flags

  • codecoverage: Enable code coverage (only possible if XDebug in installed)
    • default: false
  • showcodecoverage: Display code coverage report
    • default: true
  • showduration: Display test execution duration
    • default: true
  • showmemory: Display consumend memory
    • default: true
  • showmissingcodecoverage: Display missing code coverage
    • default: true
  • showprogress: Display test execution progress bar
    • default: true
  • branchandpathcoverage: Enable branch and path coverage
    • default: false
  • telemetry: Enable telemetry report (atoum/reports-extension must be installed)
    • default: false

15.6.3. Reports

  • codecoveragexunitpath: Path to xunit report file
  • codecoveragecloverpath: Path to clover report file
  • Code Coverage Basic
    • codecoveragereportpath: Path to HTML report
    • codecoveragereporturl: URL to HTML report
  • Code Coverage Tree Map:
    • codecoveragetreemappath: Path to tree map
    • codecoveragetreemapurl: URL to tree map
  • Code Coverage Advanced
    • codecoveragereportextensionpath: Path to HTML report
    • codecodecoveragereportextensionurl: URL to HTML report
  • Telemetry
    • telemetryprojectname: Name of telemetry report to be sent

15.7. Use with frameworks

15.7.1. Use with ezPublish

15.7.1.1. Step 1: Installation of atoum in eZ Publish

The eZ Publish framework have already a directory dedicated to tests, logically named tests. It’s in this directory that should be placed the PHAR archive of atoum. The unit test files using atoum will be placed in a subdirectory tests/atoum so they don’t conflict with the existing.

15.7.1.2. Step 2: Creating the class of the base tests

A class based on atoum must extend the class \mageekguy\atoum\test. However, this one doesn’t take into account of eZ Publish specificities. It’s therefore mandatory to define a base test class, derived from \mageekguy\atoum\test, which will take into account these specificities and will derive all of the classes of unit tests. To do this, just defined the following class in the file tests\atoum\test.php:

<?php

namespace ezp;

use mageekguy\atoum;

require_once __DIR__ . '/atoum.phar';

// Autoloading : eZ
require 'autoload.php';

if ( !ini_get( "date.timezone" ) )
{
        date_default_timezone_set( "UTC" );
}

require_once( 'kernel/common/i18n.php' );

\eZContentLanguage::setCronjobMode();

/**
 * @abstract
 */
abstract class test extends atoum\test
{
}

?>

15.7.1.3. Step 3: Creating a test class

By default, atoum asks that unit tests classes are in a namespace containing test(s)unit(s), in order to deduce the name of the tested class. For example, the namespace nameofprojet will be used in the following. For simplicity, it’s further advisable to model the test tree on the tested classes tree, in order to quickly locate the class of a tested class, and vice versa.

<?php

namespace nameofproject\tests\units;

require_once '../test.php';

use ezp;

class cache extends ezp\test
{
   public function testClass()
   {
          $this->assert->hasMethod('__construct');
   }
}

15.7.1.4. Step 4: Running the unit tests

Once a test class created, simply execute this command-line to start the test from the root of the project:

# php tests/atoum/atoum.phar -d tests/atoum/units

Thanks to Jérémy Poulain for this tutorial.

15.7.2. Use with Symfony 2

If you want to use atoum within your Symfony projects, you can install the Bundle AtoumBundle.

If you want to install and configure atoum manually, here’s how to do it.

15.7.2.1. Step 1: installation of atoum

If you use Symfony 2.0, download the PHAR and place it in the vendor directory which is at the root of your project.

If you use Symfony 2.1+, add atoum in your composer.json.

15.7.2.2. Step 2: create the test class

Imagine that we wanted to test this Entity:

<?php
// src/Acme/DemoBundle/Entity/Car.php
namespace Acme\DemoBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Acme\DemoBundle\Entity\Car
 * @ORM\Table(name="car")
 * @ORM\Entity(repositoryClass="Acme\DemoBundle\Entity\CarRepository")
 */
class Car
{
    /**
     * @var integer $id
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $name
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @var integer $max_speed
     * @ORM\Column(name="max_speed", type="integer")
     */

    private $max_speed;
}

Note

For more information about creating Entity in Symfony 2, refer to the official documentation

Create the directory Tests/Units in your Bundle (for example src/Acme/DemoBundle/Tests/Units). It’s in this directory that will be stored all tests of this Bundle.

Create a Test.php file that will serve as a base for all new tests in this Bundle.

<?php
// src/Acme/DemoBundle/Tests/Units/Test.php
namespace Acme\DemoBundle\Tests\Units;

// It includes the class loader and active it
require_once __DIR__ . '/../../../../../vendor/symfony/symfony/src/Symfony/Component/ClassLoader/UniversalClassLoader.php';

$loader = new \Symfony\Component\ClassLoader\UniversalClassLoader();

$loader->registerNamespaces(
    array(
        'Symfony'         => __DIR__ . '/../../../../../vendor/symfony/src',
        'Acme\DemoBundle' => __DIR__ . '/../../../../../src'
    )
);

$loader->register();

use mageekguy\atoum;

// For Symfony 2.0 only !
require_once __DIR__ . '/../../../../../vendor/atoum.phar';

abstract class Test extends atoum
{
    public function __construct(
        adapter $adapter = null,
        annotations\extractor $annotationExtractor = null,
        asserter\generator $asserterGenerator = null,
        test\assertion\manager $assertionManager = null,
        \closure $reflectionClassFactory = null
    )
    {
        $this->setTestNamespace('Tests\Units');
        parent::__construct(
            $adapter,
            $annotationExtractor,
            $asserterGenerator,
            $assertionManager,
            $reflectionClassFactory
        );
    }
}

Note

The inclusion of atoum’s PHAR archive is only necessary for Symfony 2.0. Remove this line if you use Symfony 2.1+.

Note

By default, atoum uses namespace tests/units for testing. However Symfony 2 and its class loader require capitalization at the beginning of the names. For this reason, we change tests namespace through the method: setTestNamespace(‘TestsUnits’).

15.7.2.3. Step 3: write a test

In the Tests/Units directory, simply recreate the tree of the classes that you want to test (for example src/Acme/DemoBundle/Tests/Units/Entity/Car.php).

Create our test file:

<?php
// src/Acme/DemoBundle/Tests/Units/Entity/Car.php
namespace Acme\DemoBundle\Tests\Units\Entity;

require_once __DIR__ . '/../Test.php';

use Acme\DemoBundle\Tests\Units\Test;

class Car extends Test
{
    public function testGetName()
    {
        $this
            ->if($car = new \Acme\DemoBundle\Entity\Car())
            ->and($car->setName('Batmobile'))
                ->string($car->getName())
                    ->isEqualTo('Batmobile')
                    ->isNotEqualTo('De Lorean')
        ;
    }
}

15.7.2.4. Step 4: launch tests

If you use Symfony 2.0:

# Launch tests of one file
$ php vendor/atoum.phar -f src/Acme/DemoBundle/Tests/Units/Entity/Car.php

# Launch all tests of the Bundle
$ php vendor/atoum.phar -d src/Acme/DemoBundle/Tests/Units

If you use Symfony 2.1+:

# Launch tests of one file
$ ./bin/atoum -f src/Acme/DemoBundle/Tests/Units/Entity/Car.php

# Launch all tests of the Bundle
$ ./bin/atoum -d src/Acme/DemoBundle/Tests/Units

Note

You can get more information on the test launch in the chapter which is dedicated to.

In any case, this is what you should get:

> PHP path: /usr/bin/php
> PHP version:
> PHP 5.3.15 with Suhosin-Patch (cli) (built: Aug 24 2012 17:45:44)
===================================================================
> Copyright (c) 1997-2012 The PHP Group
=======================================
> Zend Engine v2.3.0, Copyright (c) 1998-2012 Zend Technologies
===============================================================
>     with Xdebug v2.1.3, Copyright (c) 2002-2012, by Derick Rethans
====================================================================
> Acme\DemoBundle\Tests\Units\Entity\Car...
[S___________________________________________________________][1/1]
> Test duration: 0.01 second.
=============================
> Memory usage: 0.50 Mb.
========================
> Total test duration: 0.01 second.
> Total test memory usage: 0.50 Mb.
> Code coverage value: 42.86%
> Class Acme\DemoBundle\Entity\Car: 42.86%
==========================================
> Acme\DemoBundle\Entity\Car::getId(): 0.00%
--------------------------------------------
> Acme\DemoBundle\Entity\Car::setMaxSpeed(): 0.00%
--------------------------------------------------
> Acme\DemoBundle\Entity\Car::getMaxSpeed(): 0.00%
--------------------------------------------------
> Running duration: 0.24 second.
Success (1 test, 1/1 method, 0 skipped method, 4 assertions) !

15.7.3. Use with symfony 1.4

If you want to use atoum inside your Symfony 1.4 project, you can install the plugins sfAtoumPlugin. It’s available on this address: https://github.com/atoum/sfAtoumPlugin.

15.7.3.1. Installation

There are several ways to install this plugin in your project:

  • installation via composer
  • installation via git submodules
15.7.3.1.1. Using composer

Add this lines inside the composer.json file:

"require"     : {
  "atoum/sfAtoumPlugin": "*"
},

After a php composer.phar update the plugin should be in the plugin folder and atoum in the vendor folder.

Then, in your ProjectConfiguration file, you have to activate the plugin and define the atoum path.

<?php
sfConfig::set('sf_atoum_path', dirname(__FILE__) . '/../vendor/atoum/atoum');

if (sfConfig::get('sf_environment') != 'prod')
{
  $this->enablePlugins('sfAtoumPlugin');
}
15.7.3.1.2. Using a git submodule

First, install atoum as a submodule:

$ git submodule add git://github.com/atoum/atoum.git lib/vendor/atoum

Then install sfAtoumPlugin as a git submodule:

$ git submodule add git://github.com/atoum/sfAtoumPlugin.git plugins/sfAtoumPlugin

Finally, enable the plugin in in your ProjectConfiguration file:

<?php
if (sfConfig::get('sf_environment') != 'prod')
{
  $this->enablePlugins('sfAtoumPlugin');
}

15.7.3.2. Write tests

Tests must include the bootstrap file from the plugin:

<?php
require_once __DIR__ . '/../../../../plugins/sfAtoumPlugin/bootstrap/unit.php';

15.7.3.3. Launch tests

The symfony command atoum:test is available. The tests can then be launched in this way:

$ ./symfony atoum:test

All the arguments of atoum are available.

It’s therefore, for example, possible to give a configuration file like this :

php symfony atoum:test -c config/atoum/hudson.php

15.7.4. Symfony 1 plugin

To use atoum within a symfony project 1, a plug-in exists and is available at the following address: https://github.com/atoum/sfAtoumPlugin.

The instructions for installation and use are the cookbook Use with symfony 1.4 as well as on the github page.

15.7.5. Symfony 2 bundle

To use atoum inside a Symfony 2 project, the bundle AtoumBundle is available.

The instructions for installation and use are the cookbook Use with Symfony 2 as well as on the github page.

15.7.6. Zend Framework 2 component

If you want to use atoum within a Zend Framework 2 project, a component exists and is available at the following address.

The instructions for installation and usage are available on this page.