Skip to content

Instantly share code, notes, and snippets.

@webmozart
Created February 20, 2010 18:40
Show Gist options
  • Save webmozart/309820 to your computer and use it in GitHub Desktop.
Save webmozart/309820 to your computer and use it in GitHub Desktop.

Symfony 2 Testing Conventions

Symfony 2 is programmatically tested using unit tests. You can read more about unit testing on Wikipedia.

Test Organization

Directory Structure

The src/ directory contains the three subdirectories Components/, Foundation/ and Framework/, which contains the core bundles. The components and core bundles should have a subdirectory Tests/ which contains all the tests for the component/bundle. The foundation tests belong in the subdirectory Tests/ of the Foundation/ directory.

Some examples:

  • src/Symfony/Components/EventDispatcher/EventDispatcher.php should be tested in src/Symfony/Components/EventDispatcher/Tests/EventDispatcherTest.php
  • src/Symfony/Framework/WebBundle/User.php should be tested in src/Symfony/Framework/WebBundle/Tests/UserTest.php
  • src/Symfony/Foundation/ClassLoader.php should be tested in src/Symfony/Foundation/Tests/ClassLoaderTest.php

The subdirectory structure in the Tests/ directories should match the directory structure of the classes.

Example:

  • src/Symfony/Foundation/Bundle/Bundle.php should be tested in src/Symfony/Foundation/Tests/Bundle/BundleTest.php

Namespaces

The namespaces of the test classes should match their directory structure.

Example:

  • The namespace of src/Symfony/Foundation/Tests/Bundle/BundleTest.php is Symfony\Foundation\Tests\Bundle

Test Suites

Every Tests/ directory should contain a class AllTests that contains the suite of the component/bundle. You can use the following template for this file: (make sure you adapt the namespace)

<?php

namespace Symfony\Framework\{MyBundle}\Tests;

require_once 'PHPUnit/Framework.php';

class AllTests
{
  public static function suite()
  {
    $suite = new \PHPUnit_Framework_TestSuite('Components');

    $directoryIterator = new \RecursiveDirectoryIterator(__DIR__);
    $recursiveIterator = new \RecursiveIteratorIterator($directoryIterator);
    $filteredIterator = new \RegexIterator($recursiveIterator, '/Test\.php$/');

    $suite->addTestFiles(iterator_to_array($filteredIterator));

    return $suite;
  }
}

Test Bootstrapping

Every Tests/ directory of a component or bundle should contain a file TestInit.php that bootstraps the tests. This file must be included via require_once in every test case. You can use the following templates, which will be sufficient for most cases.

TestInit.php for components:

<?php

/*
 * This file bootstraps the test environment.
 */
namespace Symfony\Components\Console\Tests;

error_reporting(E_ALL | E_STRICT);

require_once 'PHPUnit/Framework.php';
require_once __DIR__ . '/ClassLoader.php';

$classLoader = new ClassLoader();
$classLoader->registerNamespace('Symfony\Components', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..');
$classLoader->register();

Make sure you adjust the namespace. Additionally, copy the file ClassLoader.php of the Foundation package to the Tests/ directory of the component and adjust its namespace.

TestInit.php for bundles:

<?php

/*
 * This file bootstraps the test environment.
 */

if (!isset($_SERVER['SYMFONY']))
{
  throw new \RuntimeException(
<<<EOF
Please set the environment variable SYMFONY to point to the Symfony 2 src/ directory.
On Unix, you can use the command
  export SYMFONY=/path/to/symfony/src
On Windows, you can use the command
  set SYMFONY=\path\to\symfony\src

EOF
  );
}

require_once $_SERVER['SYMFONY'] . '/Symfony/Tests/TestInit.php';

Writing Tests

A few conventions should be taken care of when writing tests files.

Test Classes

Test classes should have the suffix Test and generally inherit PHPUnit_Framework_TestCase. The name of a test class should refer to a class or the state or aspect of a class.

Some examples:

  • FormFieldTest is a good name because it refers to the FormField class
  • FormFieldUnboundTest is a good name because it refers to the "unbound" state of the FormField class
  • FormFieldCreate is a bad name, because it's too generic

Test Methods

Methods should support agile documentation and should be named so that if it fails, it is obvious what failed. They should also give information of the system they test

For example the method test name testBindInvalidData() is a good name.

Test method names can be long, but the method content should not be. If you need several assert-calls, divide the method into smaller methods. There should never be assertions within any loops, and rarely within functions.

NOTE Commonly used testing method naming convention test[methodName] is not allowed in Symfony 2. So in this case testBind() would not be allowed!

Stub Classes

If a test requires a temporary class for testing purposes, this class should be declared in the same file as the test class. The stub class should be prefixed with the name of the test and an underscore to avoid naming collisions.

Example:

The test is called FooTest, so the stub class may be called FooTest_MyStubClass.

Test Fixtures

Shared test fixtures should be set up and deleted in the methods setUp() and tearDown().

Testing for Exceptions

Exceptions should be tested using the method setExpectedException() instead of using the annotation @expectedException. The expected exception should be set exactly before the code is executed that is expected to throw the exception.

Example:

class FooTest extends \PHPUnit_Framework_TestCase
{
  public function testDoSomethingFailsIfCloseWasCalled()
  {
    $foo = new Foo();
    $foo->close();

    $this->setExpectedExeption('LogicException');
    $foo->doSomething();
  }
}

Running Tests

Tests can be executed via the phpunit command. If you have not already, install PHPUnit via PEAR.

Then you can execute all tests of Symfony 2:

$ cd src/Symfony/Tests
$ phpunit --configuration=configuration.xml

(unfortunately you have to switch into the Tests/ directory to execute PHPUnit)

You can also execute the tests of the foundation or of a specific component or bundle:

$ phpunit src/Symfony/Foundation/Tests/AllTests.php
$ phpunit src/Symfony/Components/EventDispatcher/Tests/AllTests.php
$ phpunit src/Symfony/Framework/WebBundle/Tests/AllTests.php

The last option is to execute the tests of all components or all core bundles:

$ phpunit src/Symfony/Components/Tests/AllTests.php
$ phpunit src/Symfony/Framework/Tests/AllTests.php
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment