Введение: почему ваш код заслуживает уважения (и тестов)
Представьте: вы пишете код, он работает, всё отлично. Но через месяц вы возвращаетесь к проекту, добавляете новую фичу — и вдруг всё ломается. Знакомо? Если да, то 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:
<