Что такое PHPUnit и зачем он нужен?
PHPUnit — это фреймворк для модульного тестирования (unit testing) на языке PHP. Он является частью семейства xUnit и предоставляет разработчикам инструменты для автоматизации проверки работоспособности отдельных компонентов программы (классов, методов, функций).
Зачем нужен PHPUnit?
- Раннее обнаружение ошибок: Тесты запускаются после каждого изменения кода, позволяя моментально выявить регрессии.
- Документация кода: Хорошо написанные тесты служат живой документацией, показывающей, как должен работать тот или иной метод.
- Рефакторинг без страха: Вы можете смело переписывать код, будучи уверенным, что тесты не дадут сломать существующую функциональность.
- Поддержка TDD (Test-Driven Development): Разработка через тестирование — методология, при которой сначала пишется тест, а затем минимальный код для его прохождения.
- Интеграция с CI/CD: PHPUnit легко встраивается в пайплайны непрерывной интеграции (GitHub Actions, GitLab CI, Jenkins).
PHPUnit поддерживает моки (mock-объекты), фикстуры (наборы данных для тестов), покрытие кода (code coverage) и множество других продвинутых функций.
Установка PHPUnit
PHPUnit устанавливается через Composer — менеджер зависимостей для PHP. Если у вас ещё нет Composer, установите его с официального сайта getcomposer.org.
Глобальная установка (рекомендуется для CLI)
composer global require phpunit/phpunit
После этого убедитесь, что директория глобальных пакетов Composer добавлена в PATH. Проверьте установку:
phpunit --version
Локальная установка (внутри проекта)
composer require --dev phpunit/phpunit
После этого PHPUnit будет доступен через vendor/bin/phpunit.
Установка конкретной версии
composer require --dev phpunit/phpunit:^10.0
Важно: Версия PHPUnit должна соответствовать версии PHP в вашем проекте. Например, PHPUnit 10 требует PHP 8.1 или выше.
Быстрый старт с примером кода
Создадим простой класс Calculator.php и напишем для него тесты.
Шаг 1. Создаём класс для тестирования
<?php
// src/Calculator.php
class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
public function subtract(int $a, int $b): int
{
return $a - $b;
}
public function multiply(int $a, int $b): int
{
return $a * $b;
}
public function divide(int $a, int $b): float
{
if ($b === 0) {
throw new \InvalidArgumentException("Division by zero");
}
return $a / $b;
}
}
Шаг 2. Создаём тестовый класс
<?php
// tests/CalculatorTest.php
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
private Calculator $calculator;
protected function setUp(): void
{
$this->calculator = new Calculator();
}
public function testAdd(): void
{
$result = $this->calculator->add(2, 3);
$this->assertEquals(5, $result);
}
public function testSubtract(): void
{
$result = $this->calculator->subtract(10, 4);
$this->assertEquals(6, $result);
}
public function testMultiply(): void
{
$result = $this->calculator->multiply(3, 4);
$this->assertEquals(12, $result);
}
public function testDivide(): void
{
$result = $this->calculator->divide(10, 2);
$this->assertEquals(5.0, $result);
}
public function testDivideByZero(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->calculator->divide(10, 0);
}
}
Шаг 3. Запускаем тесты
vendor/bin/phpunit tests/CalculatorTest.php
Вы должны увидеть зелёный вывод с надписью "OK (5 tests, 5 assertions)".
Основные методы/функции PHPUnit
Ниже приведены ключевые методы (assertions), которые вы будете использовать в 90% случаев.
1. assertEquals()
Что делает: Проверяет, что два значения равны (сравнение через оператор ==).
Параметры:
mixed $expected— ожидаемое значениеmixed $actual— фактическое значениеstring $message = ''— сообщение при ошибке (опционально)
Пример:
$this->assertEquals(5, 2 + 3);
$this->assertEquals('hello', strtolower('HELLO'));
2. assertSame()
Что делает: Проверяет строгое равенство (сравнение через ===). Различает 0 и false, null и пустую строку.
Параметры: Аналогично assertEquals.
Пример:
$this->assertSame(0, 0); // пройдёт
$this->assertSame(0, false); // не пройдёт, хотя assertEquals пройдёт
3. assertTrue() / assertFalse()
Что делает: Проверяет, что выражение истинно или ложно.
Параметры:
bool $condition— проверяемое условиеstring $message = ''
Пример:
$this->assertTrue(5 > 3);
$this->assertFalse(empty([])); // пустой массив считается empty, но assertFalse пройдёт
4. assertNull() / assertNotNull()
Что делает: Проверяет, является ли значение null или не null.
Параметры:
mixed $variable— проверяемая переменнаяstring $message = ''
Пример:
$this->assertNull(null);
$this->assertNotNull('not null');
5. assertCount()
Что делает: Проверяет, что количество элементов в массиве или объекте Countable равно ожидаемому.
Параметры:
int $expectedCount— ожидаемое количествоCountable|array $haystack— массив или объектstring $message = ''
Пример:
$this->assertCount(3, [1, 2, 3]);
$this->assertCount(0, []);
6. assertContains()
Что делает: Проверяет, что значение присутствует в массиве (или строке).
Параметры:
mixed $needle— искомое значениеiterable $haystack— массив или строкаstring $message = ''
Пример:
$this->assertContains(2, [1, 2, 3]);
$this->assertContains('world', 'hello world');
7. assertInstanceOf()
Что делает: Проверяет, что объект является экземпляром указанного класса или интерфейса.
Параметры:
string $expected— имя классаobject $actual— проверяемый объектstring $message = ''
Пример:
$this->assertInstanceOf(Calculator::class, $this->calculator);
$this->assertInstanceOf(Countable::class, new ArrayObject());
8. expectException()
Что делает: Указывает, что следующий код должен выбросить исключение указанного типа.
Параметры:
string $exceptionClass— класс ожидаемого исключения
Пример:
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Division by zero');
$this->calculator->divide(10, 0);
9. assertArrayHasKey()
Что делает: Проверяет, что массив содержит указанный ключ.
Параметры:
mixed $key— искомый ключarray $array— массивstring $message = ''
Пример:
$this->assertArrayHasKey('name', ['name' => 'John', 'age' => 30]);
10. assertStringContainsString()
Что делает: Проверяет, что строка содержит подстроку (регистрозависимо).
Параметры:
string $needle— искомая подстрокаstring $haystack— строка для поискаstring $message = ''
Пример:
$this->assertStringContainsString('PHP', 'PHPUnit is great');
Реальный практический пример использования
Предположим, у нас есть интернет-магазин. Напишем тесты для класса OrderProcessor, который обрабатывает заказы.
Класс OrderProcessor
<?php
// src/OrderProcessor.php
class OrderProcessor
{
private array $items = [];
private float $total = 0.0;
private bool $paid = false;
public function addItem(string $name, float $price, int $quantity = 1): void
{
$this->items[] = [
'name' => $name,
'price' => $price,
'quantity' => $quantity
];
$this->total += $price * $quantity;
}
public function getTotal(): float
{
return $this->total;
}
public function getItemCount(): int
{
return count($this->items);
}
public function pay(float $amount): void
{
if ($amount < $this->total) {
throw new \RuntimeException("Insufficient funds");
}
$this->paid = true;
}
public function isPaid(): bool
{
return $this->paid;
}
public function getItems(): array
{
return $this->items;
}
}
Тесты для OrderProcessor
<?php
// tests/OrderProcessorTest.php
use PHPUnit\Framework\TestCase;
class OrderProcessorTest extends TestCase
{
private OrderProcessor $processor;
protected function setUp(): void
{
$this->processor = new OrderProcessor();
}
public function testAddItem(): void
{
$this->processor->addItem('Laptop', 999.99, 1);
$this->assertCount(1, $this->processor->getItems());
$this->assertEquals(999.99, $this->processor->getTotal());
}
public function testMultipleItems(): void
{
$this->processor->addItem('Mouse', 25.50, 2);
$this->processor->addItem('Keyboard', 75.00, 1);
$this->assertCount(2, $this->processor->