Testing
A test is code that checks other code. Symfony uses PHPUnit and adds a test client to it: the client opens the application's pages without a real browser and checks the responses.
Installation
composer require --dev symfony/test-packTests live in the tests folder and are run with:
php bin/phpunitA unit test
It checks one class apart from the whole application:
// tests/Service/PriceCalculatorTest.php
namespace App\Tests\Service;
use App\Service\PriceCalculator;
use PHPUnit\Framework\TestCase;
class PriceCalculatorTest extends TestCase
{
public function testAddsTax(): void
{
$calculator = new PriceCalculator();
$this->assertSame(120.0, $calculator->withTax(100));
}
}The method name starts with test. Such tests run instantly.
A page test
php bin/console make:test WebTestCase BlogControllerTest// tests/Controller/BlogControllerTest.php
namespace App\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class BlogControllerTest extends WebTestCase
{
public function testIndexPageOpens(): void
{
$client = static::createClient();
$client->request('GET', '/blog');
$this->assertResponseIsSuccessful();
$this->assertSelectorTextContains('h1', 'Posts');
}
}The client performs a request inside the application, and the assertions look at the response code and the markup.
Useful assertions
| Assertion | What it claims |
|---|---|
assertResponseIsSuccessful() | A 2xx response code |
assertResponseStatusCodeSame(404) | An exact code |
assertResponseRedirects('/login') | A redirect |
assertSelectorExists('.post') | The element is on the page |
assertSelectorTextContains('h1', '…') | The element's text |
Submitting a form
public function testCreatesPost(): void
{
$client = static::createClient();
$client->request('GET', '/blog/new');
$client->submitForm('Save', [
'post[title]' => 'A test post',
'post[body]' => 'Text',
]);
$this->assertResponseRedirects();
}The first argument of submitForm() is the button's label.
Logging a user in
$client = static::createClient();
$user = static::getContainer()->get(UserRepository::class)->findOneBy(['email' => 'admin@example.com']);
$client->loginUser($user);
$client->request('GET', '/admin/posts');
$this->assertResponseIsSuccessful();The test database
Settings for tests go into the .env.test file. Symfony adds _test to the database name by itself, so real data is not harmed:
php bin/console --env=test doctrine:database:create
php bin/console --env=test doctrine:migrations:migrateCommon mistake
Tests must not depend on each other. If one test creates a record and another expects to find it, they break when run in a different order. Every test prepares its own data. The dama/doctrine-test-bundle package wraps each test in a transaction and rolls it back — the database stays clean after a test.