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

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

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

Зачем это нужно? Во-первых, чтобы не бояться менять код. Во-вторых, чтобы новые фичи не ломали старые. В-третьих, чтобы коллеги (или ты сам через месяц) не проклинали всё на свете. PHPUnit — стандарт де-факто в PHP-мире, его используют Laravel, Symfony и другие крупные проекты.

Как установить PHPUnit: три способа для ленивых

Установка — это просто. Вот три варианта, от простого к сложному:

СпособКомандаКогда использовать
Глобально через Composercomposer global require phpunit/phpunitЕсли хочешь запускать тесты из любой папки
Локально в проектcomposer require --dev phpunit/phpunitСтандартный способ для конкретного проекта
Скачать PHAR-архивСкачать с phpunit.deЕсли нет Composer или нужна изолированная версия

Я рекомендую локальную установку через Composer — это чище и удобнее. После установки проверь: ./vendor/bin/phpunit --version.

Пишем первый тест: от простого к сложному

Давай напишем что-то реальное. Допустим, у нас есть класс, который считает скидку:

<?php

class DiscountCalculator
{
    public function calculate(int $price, int $percent): int
    {
        if ($percent < 0 || $percent > 100) {
            throw new InvalidArgumentException('Percent must be between 0 and 100');
        }
        return (int) ($price * (100 - $percent) / 100);
    }
}

Теперь напишем тест. Создаём файл tests/DiscountCalculatorTest.php:

<?php

use PHPUnit\Framework\TestCase;

class DiscountCalculatorTest extends TestCase
{
    public function testCalculateReturnsDiscountedPrice(): void
    {
        $calculator = new DiscountCalculator();
        $result = $calculator->calculate(1000, 20);
        $this->assertEquals(800, $result);
    }

    public function testCalculateWithZeroPercent(): void
    {
        $calculator = new DiscountCalculator();
        $result = $calculator->calculate(500, 0);
        $this->assertEquals(500, $result);
    }

    public function testCalculateThrowsExceptionForInvalidPercent(): void
    {
        $this->expectException(InvalidArgumentException::class);
        $calculator = new DiscountCalculator();
        $calculator->calculate(100, 150);
    }
}

Запускаем: ./vendor/bin/phpunit tests/DiscountCalculatorTest.php. Если всё зелёное — ты красавчик. Если красное — ищи ошибку.

Лайфхак: Называй тесты так, чтобы по имени было понятно, что проверяется. Например, testCalculateReturnsDiscountedPrice — сразу ясно, что тест проверяет возврат цены со скидкой. Избегай имён вроде testOne или testFunction.

Основные assert-методы: твой арсенал проверок

PHPUnit предоставляет кучу методов для проверок. Вот самые популярные:

МетодЧто проверяетПример
assertEqualsРавенство значений (с учётом типа)$this->assertEquals(10, $result);
assertSameСтрогое равенство (===)$this->assertSame(10, $result);
assertTrueЗначение true$this->assertTrue($isValid);
assertFalseЗначение false$this->assertFalse($hasError);
assertNullЗначение null$this->assertNull($data);
assertInstanceOfОбъект определённого класса$this->assertInstanceOf(User::class, $user);
expectExceptionОжидание исключения$this->expectException(\InvalidArgumentException::class);

Запомни: assertEquals и assertSame — твои лучшие друзья. Первый — для обычных случаев, второй — когда важна строгость (например, для булевых значений).

Правила хорошего тона в тестировании

Тесты — это тоже код, и за ним нужно ухаживать. Вот несколько правил, которые я вынес за 10 лет:

  • Один тест — одна проверка. Не пиши гигантские тесты, которые проверяют всё подряд. Легче найти ошибку, когда каждый тест отвечает за одно.
  • Изолируй тесты. Не используй глобальные переменные или общие ресурсы без сброса. Каждый тест должен начинаться с чистого листа.
  • Тестируй краевые случаи. Ноль, пустая строка, отрицательные числа, максимум — именно там чаще всего живут баги.
  • Не тестируй фреймворк. PHPUnit не должен проверять, работает ли PDO или Symfony. Тестируй только свой код.
  • Поддерживай тесты в актуальном состоянии. Если меняешь логику — обнови тесты. Иначе они превратятся в бесполезный мусор.
Правило: Если ты не написал тест для новой функции, считай, что она не работает. Это звучит жёстко, но это правда. Без теста ты никогда не узнаешь, сломается ли код завтра.

Data Providers: когда лень писать много тестов

Иногда нужно проверить одну и ту же функцию с разными данными. Вручную писать 10 тестов — скучно. PHPUnit предлагает Data Providers. Это методы, которые возвращают массивы с данными. Пример:

<?php

use PHPUnit\Framework\TestCase;

class DiscountCalculatorTest extends TestCase
{
    /** @dataProvider discountDataProvider */
    public function testCalculateWithMultipleValues(int $price, int $percent, int $expected): void
    {
        $calculator = new DiscountCalculator();
        $result = $calculator->calculate($price, $percent);
        $this->assertEquals($expected, $result);
    }

    public function discountDataProvider(): array
    {
        return [
            [1000, 20, 800],
            [500, 0, 500],
            [200, 100, 0],
            [0, 50, 0],
            [100, 1, 99],
        ];
    }
}

Теперь один тест запускается 5 раз с разными параметрами. Экономия времени и нервов.

Резюме: что в итоге

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

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

Студия WNDER