TDD в PHP: как тесты превращают код в искусство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему ваш код заслуживает уважения (и тестов)

Представьте: вы пишете код, он работает, всё отлично. Но через месяц вы возвращаетесь к проекту, добавляете новую фичу — и вдруг всё ломается. Знакомо? Если да, то TDD в PHP — это то, что вам нужно. Разработка через тестирование (Test-Driven Development) — это не просто модный тренд, а философия, которая меняет отношение к коду. В этой статье мы разберём, как TDD превращает хаос в порядок, а баги — в артефакты прошлого. Вы узнаете, как писать тесты на PHP, которые реально экономят время, и увидите это на примерах кода. Поехали!

Что такое TDD и зачем он нужен в PHP?

TDD в PHP — это методология, где вы сначала пишете тест, а потом код, который этот тест проходит. Звучит странно? На самом деле это гениально. Вместо того чтобы гадать, работает ли ваш код, вы получаете мгновенную обратную связь. Основные принципы TDD:

  • Красный — напишите тест, который падает (потому что код ещё не написан).
  • Зелёный — напишите минимальный код, чтобы тест прошёл.
  • Рефакторинг — улучшите код, не ломая тестов.

Этот цикл повторяется снова и снова. В PHP TDD особенно полезен, потому что язык часто используется для веб-приложений, где ошибки могут стоить дорого. Тесты на PHPUnit (основной фреймворк для тестирования) помогают избежать регрессий и делают код прозрачным. Плюс, TDD дисциплинирует: вы начинаете думать о дизайне до того, как пишете код.

Настройка окружения для тестирования PHP

Прежде чем погрузиться в примеры, давайте подготовим среду. Для разработки через тестирование PHP нам понадобится PHPUnit. Установка проста:

composer require --dev phpunit/phpunit ^10

Создайте папку tests в корне проекта. Внутри — файл CalculatorTest.php. Убедитесь, что у вас установлен PHP 8.0+. Теперь всё готово для первого теста.

Первый тест: пишем калькулятор с TDD

Давайте создадим простой калькулятор. Сначала — тест. Это классический пример TDD PHP для начинающих.

Шаг 1: Красный (пишем падающий тест)

Создаём tests/CalculatorTest.php:

<?php

use PHPUnit\Framework\TestCase;

class CalculatorTest extends TestCase
{
    public function testAddition()
    {
        $calculator = new Calculator();
        $result = $calculator->add(2, 3);
        $this->assertEquals(5, $result);
    }
}

Запускаем тест:

./vendor/bin/phpunit tests/CalculatorTest.php

Ожидаем ошибку: Class "Calculator" not found. Всё по плану — это красная фаза.

Шаг 2: Зелёный (пишем минимальный код)

Создаём src/Calculator.php:

<?php

class Calculator
{
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }
}

Не забудьте подключить автозагрузку в тесте:

require_once __DIR__ . '/../src/Calculator.php';

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

Шаг 3: Рефакторинг с уверенностью

Допустим, мы хотим добавить проверку типов. Меняем код:

public function add(int $a, int $b): int
{
    if (!is_int($a) || !is_int($b)) {
        throw new InvalidArgumentException('Only integers allowed');
    }
    return $a + $b;
}

Запускаем тесты — они всё ещё проходят. TDD даёт вам смелость экспериментировать.

Продвинутый TDD: работа с исключениями и моками

Когда вы освоите базу, переходите к сложным сценариям. Например, тестирование исключений. В unit тестах PHP это делается так:

public function testDivisionByZero()
{
    $this->expectException(\DivisionByZeroError::class);
    $calculator = new Calculator();
    $calculator->divide(10, 0);
}

А для внешних сервисов используйте моки. PHPUnit поддерживает их из коробки:

public function testUserService()
{
    $mockRepository = $this->createMock(UserRepository::class);
    $mockRepository->method('findById')
        ->willReturn(new User(1, 'John'));

    $service = new UserService($mockRepository);
    $user = $service->getUser(1);
    $this->assertEquals('John', $user->getName());
}

Это ключевой навык для тестирования кода PHP в реальных проектах.

Почему TDD — это не про скорость, а про качество?

Многие думают: «TDD замедляет разработку». Да, первое время вы будете писать больше кода. Но посмотрите на долгосрочную перспективу. Test driven development PHP сокращает время на отладку в 3-5 раз. Вы не тратите часы на поиск багов в продакшене — тесты ловят их на стадии разработки. Плюс, TDD улучшает архитектуру: вы не можете написать тест для монолитного spaghetti-кода, поэтому вы вынуждены делать модули чистыми.

Пример из жизни: проект на PHP с TDD потребовал 20% больше времени на старте, но финальная стадия заняла на 40% меньше. Итог — экономия. Это ли не повод попробовать?

Частые ошибки при TDD в PHP (и как их избежать)

  • Слишком большие тесты. Один тест — одна проверка. Не пытайтесь тестировать всё сразу.
  • Игнорирование рефакторинга. Зелёный тест — не конец. Улучшайте код, пока тесты проходят.
  • Тестирование приватных методов. Не надо. Тестируйте публичный интерфейс.
  • Спам в тестах. Не пишите тесты ради тестов. Каждый тест должен иметь смысл.

Если вы новичок в PHP тестировании, начните с малого: один класс, один тест. Постепенно усложняйте.

TDD и фреймворки: Laravel, Symfony, Yii

В современных фреймворках TDD встроен в культуру. Например, в Laravel есть встроенный PHPUnit и фасады для моков. Вот пример теста для Laravel:

<

Блоги

Рекомендаций по книгам