Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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-pack

Tests live in the tests folder and are run with:

php bin/phpunit

A 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

AssertionWhat 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:migrate

Common 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.

Thanks for reading!

Donate to the site — we work hard for you.