Écrivez des tests !

Rappel des process

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Principes

Écrivez des tests !

FIRST

Principes

Écrivez des tests !

F

I

R

S

T

ast

solated

epeteable

elf-explanatory

imely

Les types de tests

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Écrivez des tests !

Unit tests

<?php

final readonly class Email
{
    public string $email;

    public function __construct(string $email)
    {
        $this->ensureIsValidEmail($email);

        $this->email = $email;
    }

    private function ensureIsValidEmail(string $email): void
    {
        if (/* is invalid */) {
            throw new RuntimeException('Invalid email');
        }
    }
}
<?php

use Email;

final class EmailTest
{
    public function test_valid_email()
    {
        $string = 'user@example.com';

		try {
        	$email = new Email($string);
        } catch (RuntimeException) {
            throw new RuntimeException('Test failed');
        }

        if ($email !== $string) {
            throw new RuntimeException('Test failed');
        }
    }

    public function test_invalid_email_throws_exception()
    {
        try {
            new Email('invalid email');
        } catch (RuntimeException) {
            return;
        }

        throw new RuntimeException('Test failed');
    }
}

Écrivez des tests !

Unit tests

<?php

class Mailer {
    public function __construct(
        private TransportInterface $transport
    ) {
    }

    public function sendEmail(Email $email)
    {
        // ...
    }
}
<?php

class EmailTest extends TestCase
{
    public function test_send_email()
    {
        $transportMock = new class() implements TransportInterface {
            private array $sent = [];

            public function send(Sendable $sendable)
            {
                $this->sent[] = $sendable;
            }

            public function getSentEmails(): array
            {
                return $this->sent;
            }
        };

        $mailer = new Mailer($transportMock);

        $mailer->sendEmail(new Email(...));

        self::assertCount(1, $transportMock->getSentEmails());
    }
}

Écrivez des tests !

Functional tests

Écrivez des tests !

Functional tests

<?php

class Mailer {
    public function __construct(
        private TransportInterface $transport
    ) {
    }

    public function sendEmail(Email $email)
    {
        // ...
    }
}
<?php

class EmailTest extends KernelTestCase
{
    public function test_send_email()
    {
        $app = self::createKernel();

        $mailer = $app->getContainer()->get(Mailer::class);

        try {
            $mailer->sendEmail(new Email(...));
        } catch (RuntimeException) {
            throw new RuntimeException('Test failed');
        }
        
        $transport = $app->getContainer()->get(TransportInterface::class);
        
        self::assertCount(1, $transport->getSentEmails());
    }
}

Écrivez des tests !

Integration tests

Écrivez des tests !

Integration tests

<?php

class RegistrationTest extends KernelTestCase
{
    public function test_email_sent_via_registration()
    {
        $client = self::createKernel()->get('test.client');

        $response = $client->request('POST', '/register', [
            'form[username]' => 'Ash',
            'form[email]' => 'ash@example.com',
            'form[password]' => 'very secured!',
        ]);

        $transport = $client->getContainer()->get(TransportInterface::class);
        self::assertCount(1, $transport->getSentEmails());
    }
}

Écrivez des tests !

Integration tests

Écrivez des tests !

Integration tests

Écrivez des tests !

End-to-end tests

Écrivez des tests !

End-to-end tests

<?php

class RegistrationTest extends E2ETestCase
{
    public function test_registration()
    {
        $client = self::createClient();

        $response = $client->request('GET', '/register');

        $driver = $response->getWebDriver();
        
        self::assertCurrentPage('/register', $driver);

        $driver->await("
            document.getElementById('form_username').value = 'Ash';
            document.getElementById('form_email').value = 'ash@example.com';
            document.getElementById('form_password').value = 'Very secured!';
            const form = document.getElementById('registration_form');
            form.submit();
        ");

        self::assertCurrentPage('/', $driver);
        self::assertPageContainsElementText(
            'div.alert.alert-success', 
            'Registration successful!', 
            $driver,
        );
    }
}
document.getElementById('form_username').value = 'Ash';
document.getElementById('form_email').value = 'ash@example.com';
document.getElementById('form_password').value = 'Very secured!';
document.getElementById('registration_form').submit();

Écrivez des tests !

Techniques

Écrivez des tests !

Mocks

Écrivez des tests !

<?php

class MyFeature
{
    public function doSomethingNow()
    {
    	$now = new DateTime();
        
        // Do something
    }
}

Mocks

Écrivez des tests !

<?php

class MyFeature
{
    private readonly Clock $clock;

    public function __construct(Clock $clock) {
    	$this->clock = $clock;
    }

    public function doSomethingNow()
    {
    	$now = $this->clock->now();
        
        // Do something
    }
}

Mocks

Écrivez des tests !

<?php

class MyFeature
{
    private readonly Clock $clock;

    public function __construct(Clock $clock) {
    	$this->clock = $clock;
    }

    public function doSomethingNow()
    {
    	$now = $this->clock->now();
        
        // Do something
    }
}
<?php

class MyFeatureTest
{
    public function testMyFeature()
    {
    	$clock = new Clock();

    	$feature = new MyFeature($clock);

        $feature->doSomething();

        // ...
    }
}

Mocks

Écrivez des tests !

<?php

class MyFeature
{
    private readonly Clock $clock;

    public function __construct(Clock $clock) {
    	$this->clock = $clock;
    }

    public function doSomethingNow()
    {
    	$now = $this->clock->now();
        
        // Do something
    }
}
<?php

class MyFeatureTest
{
    public function testMyFeature()
    {
    	$clock = new class() extends Clock
        {
        	public function now()
            {
            	return new DateTime('2025-02-18 18:30:00');
            }
        };

    	$feature = new MyFeature($clock);

        $feature->doSomething();

        // ...
    }
}

Smoke testing

💻

🖰

🔥

Écrivez des tests !

Smoke testing

Écrivez des tests !

Golden Master

Écrivez des tests !

Continuous integration

Écrivez des tests !

Github Actions

CircleCI

Gitlab CI

Atlassian Bamboo

Jenkins

AppVeyor

Stop talking, start testing!

Écrivez des tests !

@pierstoval

Alex Rock

Freelance dev, architect, coach & trainer

Merci !

Écrivez des tests !

By Alex Rock

Écrivez des tests !

-

  • 30