Перейти к основному содержимому

PHPUnit и тестирование PHP

Разработчику Архитектору

PHPUnit и тестирование PHP

PHPUnit — стандарт xUnit для PHP: классы-тесты, утверждения (assert*), моки, data providers. Pest — синтаксис поверх PHPUnit (стиль Jest). В Laravel и Symfony тесты уже встроены в скелет проекта.

Обзор в экосистеме: Экосистема PHP-приложений. Общая теория тестирования: раздел тестирования.


Зачем тестировать PHP-код

УровеньЧто проверяемПример
UnitОдна функция/класс изолированнорасчёт скидки, валидатор
IntegrationНесколько слоёв + БДрепозиторий + SQLite in-memory
Feature / HTTPМаршрут end-to-endGET /api/users → 200 + JSON

Автотесты ловят регрессии при рефакторинге и документируют ожидаемое поведение.


Минимальный проект на PHPUnit

mkdir php-tests && cd php-tests
composer init -n
composer require --dev phpunit/phpunit ^11

composer.json — autoload для тестов:

{
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}

src/Calculator.php:

<?php

namespace App;

final class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
}

tests/CalculatorTest.php:

<?php

namespace Tests;

use App\Calculator;
use PHPUnit\Framework\TestCase;

final class CalculatorTest extends TestCase
{
public function test_adds_two_integers(): void
{
$calc = new Calculator();
$this->assertSame(5, $calc->add(2, 3));
}

/**
* @dataProvider additionProvider
*/
public function test_addition_provider(int $a, int $b, int $expected): void
{
$this->assertSame($expected, (new Calculator())->add($a, $b));
}

public static function additionProvider(): array
{
return [
[0, 0, 0],
[-1, 1, 0],
[100, 200, 300],
];
}
}

phpunit.xml:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php" colors="true">
<testsuites>
<testsuite name="App">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>

Запуск:

./vendor/bin/phpunit

Моки и заглушки

use PHPUnit\Framework\TestCase;

final class MailerTest extends TestCase
{
public function test_sends_welcome_email(): void
{
$mailer = $this->createMock(MailerInterface::class);
$mailer->expects($this->once())
->method('send')
->with($this->stringContains('welcome'));

(new UserService($mailer))->register('a@b.c');
}
}

createMock подменяет зависимость; expects задаёт ожидания вызовов.


Pest (кратко)

composer require pestphp/pest --dev --with-dependencies
./vendor/bin/pest --init
test('addition works', function () {
expect((new Calculator())->add(2, 3))->toBe(5);
});

Pest совместим с PHPUnit-ассертами и отчётами CI.


Тестирование Laravel

В скелете Laravel уже есть tests/Feature и tests/Unit.

// tests/Feature/HelloTest.php
namespace Tests\Feature;

use Tests\TestCase;

class HelloTest extends TestCase
{
public function test_home_returns_ok(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
php artisan test
# или
./vendor/bin/phpunit

Полезные приёмы:

  • RefreshDatabase — чистая БД на каждый тест;
  • $this->actingAs($user) — авторизованный запрос;
  • Http::fake() — подмена исходящих HTTP;
  • Queue::fake() + Queue::assertPushed(SendEmail::class) — см. Laravel — очереди и политики.

Тестирование Symfony

composer require --dev symfony/test-pack
php bin/phpunit

WebTestCase для HTTP:

$client = static::createClient();
$client->request('GET', '/hello');
$this->assertResponseIsSuccessful();

CI

В GitHub Actions типичный шаг:

- run: composer install --no-interaction
- run: ./vendor/bin/phpunit --coverage-text

Связанные материалы


См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).