Представьте, что вы написали сайт для интернет-магазина. Всё работает, клиенты довольны. Но вдруг — баг: корзина перестала считать скидку. Вы лезете в код, ищете час, два, а проблема в одной строчке. Знакомо? PHPUnit — это инструмент, который ловит такие ошибки до того, как они доберутся до пользователя. В этой статье я расскажу, как начать тестировать свой код без лишней головной боли.

Что такое PHPUnit и зачем он нужен?

PHPUnit — это фреймворк для модульного тестирования PHP-кода. Если проще: вы пишете маленькие проверки для каждой функции или метода. Эти проверки запускаются автоматически и говорят: «Всё ок» или «Тут ошибка». Это как автоинспектор для вашего кода: он проверяет, не нарушили ли вы правила.

Зачем это нужно? Допустим, вы добавили новую фичу — и сломали старую. Без тестов вы узнаете об этом только от пользователей. С тестами — сразу после запуска. Экономит нервы и время.

Установка и первый тест

Установить PHPUnit проще простого. Если у вас есть Composer (менеджер пакетов для PHP), выполните команду:

composer require --dev phpunit/phpunit

Теперь создайте файл теста. Пусть у нас есть простой класс-калькулятор:

<?php
class Calculator {
    public function add($a, $b) {
        return $a + $b;
    }
}

Тест для него будет выглядеть так:

<?php
use PHPUnit\Framework\TestCase;

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

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

vendor/bin/phpunit tests/CalculatorTest.php

Если всё верно, увидите зелёную точку. Если нет — красную букву F и сообщение об ошибке.

Правило: Называйте тестовые классы с суффиксом Test (например, UserServiceTest), а методы тестов — с префиксом test (testGetUser). Это стандарт, который понимают все PHP-разработчики.

Основные assert-методы: таблица для быстрого старта

Assert — это проверка. Вы говорите: «Я ожидаю, что результат будет равен 5». Если нет — тест падает. Вот самые полезные assert-методы:

Метод Что проверяет Пример
assertEquals Равенство значений (с учётом типа) $this->assertEquals(5, $result)
assertSame Строгое равенство (===) $this->assertSame(5, $result)
assertTrue Значение true $this->assertTrue($isValid)
assertFalse Значение false $this->assertFalse($hasError)
assertNull Значение null $this->assertNull($user)

Тестирование исключений: когда код должен падать

Иногда мы ожидаем, что код выбросит исключение. Например, если передали неверные данные. PHPUnit умеет проверять и это:

<?php
public function testDivideByZero() {
    $this->expectException(\InvalidArgumentException::class);
    $calc = new Calculator();
    $calc->divide(10, 0);
}

Если метод divide выбросит InvalidArgumentException — тест пройдёт. Если нет — упадёт. Удобно, правда?

Лайфхак: Используйте expectException перед вызовом метода, который должен выбросить исключение. Иначе тест может не сработать.

Фикстуры: готовим данные для тестов

Часто для теста нужны одинаковые объекты или база данных. Чтобы не создавать их в каждом тесте, используйте фикстуры — методы setUp и tearDown. setUp запускается перед каждым тестом, tearDown — после.

<?php
class UserTest extends TestCase {
    private $user;

    protected function setUp(): void {
        $this->user = new User('John', 'john@example.com');
    }

    public function testGetEmail() {
        $this->assertEquals('john@example.com', $this->user->getEmail());
    }

    protected function tearDown(): void {
        unset($this->user);
    }
}

Это избавляет от дублирования кода и делает тесты чище.

Моки: как тестировать код, который зависит от внешних сервисов

Представьте, что ваш класс отправляет email через внешний API. Во время теста вы не хотите реально слать письма — это медленно и может привести к спаму. Тут помогают моки (mock-объекты). Они имитируют поведение настоящего объекта.

<?php
public function testSendWelcomeEmail() {
    $mailer = $this->createMock(Mailer::class);
    $mailer->expects($this->once())
           ->method('send')
           ->willReturn(true);

    $service = new UserService($mailer);
    $result = $service->registerUser('test@example.com');
    $this->assertTrue($result);
}

Здесь мы говорим: «Ожидаем, что метод send будет вызван ровно один раз и вернёт true». Если в коде send вызовется дважды — тест упадёт.

Итоговое резюме: что в итоге

PHPUnit — это не страшно. Это просто набор правил и инструментов, которые делают ваш код надёжнее. Начните с малого: напишите тест для одной функции. Потом для класса. Потом для всего проекта. Со временем вы заметите, что багов стало меньше, а уверенности в коде — больше.

Главное — не пытайтесь покрыть тестами всё сразу. Тестируйте критичные куски: логику, расчёты, работу с БД. А мелочи — по мере сил. И помните: тесты пишутся не для галочки, а для вашего спокойствия.

Студия WNDER