Представь, что ты собрал сложный механизм, нажал кнопку пуск, а он задымился. Примерно так чувствует себя разработчик, который выкладывает код без тестов. PHPUnit — это твой огнетушитель и страховка. В этой статье я расскажу, как подружиться с ним, не сойти с ума и начать спать спокойно.
Что такое PHPUnit и зачем он вообще нужен?
PHPUnit — это фреймворк для модульного тестирования PHP-кода. Если проще: ты пишешь маленькие проверки, которые автоматически запускаются и говорят: «Всё ок» или «Сломалось вот тут». Это как чек-лист перед вылетом самолёта — лучше перепроверить, чем падать.
Зачем это нужно? Во-первых, чтобы не бояться менять код. Во-вторых, чтобы новые фичи не ломали старые. В-третьих, чтобы коллеги (или ты сам через месяц) не проклинали всё на свете. PHPUnit — стандарт де-факто в PHP-мире, его используют Laravel, Symfony и другие крупные проекты.
Как установить PHPUnit: три способа для ленивых
Установка — это просто. Вот три варианта, от простого к сложному:
| Способ | Команда | Когда использовать |
|---|---|---|
| Глобально через Composer | composer 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



