Урок 10: Тестирование в Symfony

В 10-м уроке мы перейдем к тестированию кода. В Symfony тестирование встроено в саму философию фреймворка и сейчас я покажу, как сделать ваш проект надежным и стабильным. Мы разберем установку PHPUnit, написание тестов для контроллеров, форм и сервисов, а также научимся работать с mock-объектами. В конце урока вас ждут практические задачи и примеры кода.

Установка PHPUnit

PHPUnit это стандартный инструмент для тестирования PHP-приложений. В Symfony он уже интегрирован, но давайте убедимся, что все настроено правильно.

Шаги по установке

  1. Установите PHPUnit через Composer:
bash
composer require --dev phpunit/phpunit
  1. Проверьте конфигурацию в файле phpunit.xml.dist (он должен быть в корне проекта). Убедитесь, что секция <testsuites> включает ваши тесты.
  2. Запустите тесты:
bash
./bin/phpunit

Если вы видите сообщение «No tests executed», не пугайтесь это нормально, ведь мы еще ничего не написали.

Проверка установки

Создадим первый тест в tests/ExampleTest.php:

php
use PHPUnit\Framework\TestCase;

class ExampleTest extends TestCase
{
    public function testTrueIsTrue()
    {
        $this->assertTrue(true);
    }
}

Запустите тест:

bash
./bin/phpunit tests/ExampleTest.php

Если увидите зеленую надпись «OK (1 test)», установка прошла успешно!

Написание тестов для контроллеров

Контроллеры это «лицо» вашего приложения. Их тестирование гарантирует, что страницы работают корректно.

Пример теста для контроллера

Допустим, у нас есть контроллер PostController с методом index(), который возвращает список постов.
Создадим тест в tests/Controller/PostControllerTest.php:

php
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class PostControllerTest extends WebTestCase
{
    public function testIndex()
    {
        $client = static::createClient();
        $client->request('GET', '/posts');

        $this->assertResponseIsSuccessful();
        $this->assertSelectorTextContains('h1', 'Список постов');
    }
}

Пояснение:

  • createClient() создает виртуальный браузер.
  • request() отправляет HTTP-запрос.
  • assertResponseIsSuccessful() проверяет, что статус ответа 200.
  • assertSelectorTextContains() ищет текст в HTML.

Тестирование перенаправлений

Если контроллер выполняет редирект, используйте assertResponseRedirects():

php
public function testRedirect()
{
    $client = static::createClient();
    $client->request('GET', '/old-url');

    $this->assertResponseRedirects('/new-url', 301);
}

Тестирование форм и сервисов

Тестирование форм

Предположим, у нас есть форма регистрации. Проверим ее валидацию и отправку:

php
public function testRegistrationForm()
{
    $client = static::createClient();
    $crawler = $client->request('GET', '/register');

    $form = $crawler->selectButton('Зарегистрироваться')->form();
    $form['registration[email]'] = 'not-an-email';
    $form['registration[password]'] = '123';

    $client->submit($form);

    // Проверяем, что есть ошибки валидации
    $this->assertSelectorCount(2, '.form-error');

    // Тест успешной отправки
    $form['registration[email]'] = 'user@example.com';
    $form['registration[password]'] = 'SecurePassword123!';
    $client->submit($form);

    $this->assertResponseRedirects('/login');
    // Проверяем, что пользователь создан в базе
    $user = $client->getContainer()->get('doctrine')
        ->getRepository(User::class)
        ->findOneBy(['email' => 'user@example.com']);

    $this->assertNotNull($user);
}

Тестирование сервисов

Допустим, у нас есть сервис SlugGenerator, который создает slug из заголовка:

php
// tests/Service/SlugGeneratorTest.php
use App\Service\SlugGenerator;
use PHPUnit\Framework\TestCase;

class SlugGeneratorTest extends TestCase
{
    public function testGenerateSlug()
    {
        $slugGenerator = new SlugGenerator();
        $slug = $slugGenerator->generate('Привет, Symfony!');

        $this->assertEquals('privet-symfony', $slug);
    }
}

Mock-объекты в Symfony

Mock-объекты (заглушки) позволяют изолировать тестируемый код от внешних зависимостей, например, баз данных или API.

Пример: Mock сервиса отправки email

Допустим, у нас есть сервис EmailSender, который мы не хотим вызывать в тестах.
Создадим mock-объект:

php
public function testRegistrationSendsEmail()
{
    $client = static::createClient();

    // Создаем mock для EmailSender
    $emailSenderMock = $this->createMock(EmailSender::class);
    $emailSenderMock->expects($this->once())
        ->method('sendWelcomeEmail');

    // Заменяем реальный сервис на mock
    $client->getContainer()->set(EmailSender::class, $emailSenderMock);

    // Тестируем регистрацию
    $client->request('GET', '/register');
    $form = $client->getCrawler()->selectButton('Зарегистрироваться')->form();
    $form['registration[email]'] = 'user@example.com';
    $form['registration[password]'] = 'SecurePassword123!';
    $client->submit($form);

    // Проверяем, что sendWelcomeEmail был вызван
}

Практические задачи

  1. Установите PHPUnit и запустите тест из раздела 1.
  2. Напишите тест для контроллера, который проверяет:
    • Статус ответа 200.
    • Наличие определенного текста на странице.
  3. Протестируйте форму входа:
    • Отправьте неверные данные и проверьте ошибки.
    • Отправьте верные данные и проверьте редирект.
  4. Создайте сервис для форматирования даты и напишите для него тест.
  5. Используйте mock-объект для тестирования сервиса, зависящего от внешнего API.

Теперь вы умеете писать тесты для всех компонентов Symfony! Это делает ваш код надежным и упрощает рефакторинг. Не забывайте запускать тесты перед каждым коммитом, это будет хорошей привычкой.

Хотите узнать больше? Переходите к полному курсу по Symfony для начинающих.

Поделиться статьей:
Поддержать автора блога

Поддержка автора осуществляется с помощью специальной формы ниже, предоставленной сервисом «ЮMoney». Все платёжные операции выполняются на защищённой странице сервиса, что обеспечивает их корректность и полную безопасность.

Персональные рекомендации
Оставить комментарий