Symfony 2 is programmatically tested using unit tests. You can read more about unit testing on Wikipedia.
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 insrc/Symfony/Components/EventDispatcher/Tests/EventDispatcherTest.php
src/Symfony/Framework/WebBundle/User.php
should be tested insrc/Symfony/Framework/WebBundle/Tests/UserTest.php
src/Symfony/Foundation/ClassLoader.php
should be tested insrc/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 insrc/Symfony/Foundation/Tests/Bundle/BundleTest.php
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
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;
}
}
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';
A few conventions should be taken care of when writing tests files.
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
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!
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
.
Shared test fixtures should be set up and deleted in the methods setUp() and tearDown().
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();
}
}
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