Тестування модулів
Огляд
Тестування модулів у Flight допомагає вам переконатися, що ваша програма поводиться як очікується, виявляти помилки на ранніх етапах та полегшувати підтримку вашого коду. Flight розроблено для безперебійної роботи з PHPUnit, найпопулярнішим фреймворком для тестування PHP.
Розуміння
Тести модулів перевіряють поведінку невеликих частин вашої програми (наприклад, контролерів або сервісів) в ізоляції. У Flight це означає тестування того, як ваші маршрути, контролери та логіка реагують на різні входи — без залежності від глобального стану або реальних зовнішніх сервісів.
Ключові принципи:
- Тестуйте поведінку, а не реалізацію: Зосередьтеся на тому, що робить ваш код, а не як він це робить.
- Уникайте глобального стану: Використовуйте ін'єкцію залежностей замість
Flight::set()
абоFlight::get()
. - Мокайте зовнішні сервіси: Замінюйте такі речі, як бази даних або поштові клієнти, тестовими подвійниками.
- Тримайте тести швидкими та сфокусованими: Тести модулів не повинні звертатися до реальних баз даних або API.
Основне використання
Налаштування PHPUnit
- Встановіть PHPUnit за допомогою Composer:
composer require --dev phpunit/phpunit
- Створіть директорію
tests
у корені вашого проєкту. - Додайте скрипт для тестів до вашого
composer.json
:"scripts": { "test": "phpunit --configuration phpunit.xml" }
- Створіть файл
phpunit.xml
:<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Flight Tests"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>
Тепер ви можете запускати тести за допомогою composer test
.
Тестування простого обробника маршруту
Припустимо, у вас є маршрут, який валідує email:
// index.php
$app->route('POST /register', [ UserController::class, 'register' ]);
// UserController.php
class UserController {
protected $app;
public function __construct(flight\Engine $app) {
$this->app = $app;
}
public function register() {
$email = $this->app->request()->data->email;
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->app->json(['status' => 'error', 'message' => 'Invalid email']);
}
return $this->app->json(['status' => 'success', 'message' => 'Valid email']);
}
}
Простий тест для цього контролера:
use PHPUnit\Framework\TestCase;
use flight\Engine;
class UserControllerTest extends TestCase {
public function testValidEmailReturnsSuccess() {
$app = new Engine();
$app->request()->data->email = 'test@example.com';
$controller = new UserController($app);
$controller->register();
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('success', $output['status']);
$this->assertEquals('Valid email', $output['message']);
}
public function testInvalidEmailReturnsError() {
$app = new Engine();
$app->request()->data->email = 'invalid-email';
$controller = new UserController($app);
$controller->register();
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('error', $output['status']);
$this->assertEquals('Invalid email', $output['message']);
}
}
Поради:
- Симулюйте дані POST за допомогою
$app->request()->data
. - Уникайте використання статичних методів
Flight::
у ваших тестах — використовуйте екземпляр$app
.
Використання ін'єкції залежностей для тестуємо контролерів
Інжектуйте залежності (наприклад, базу даних або поштового клієнта) у ваші контролери, щоб полегшити їх мокування в тестах:
use flight\database\PdoWrapper;
class UserController {
protected $app;
protected $db;
protected $mailer;
public function __construct($app, $db, $mailer) {
$this->app = $app;
$this->db = $db;
$this->mailer = $mailer;
}
public function register() {
$email = $this->app->request()->data->email;
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->app->json(['status' => 'error', 'message' => 'Invalid email']);
}
$this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
$this->mailer->sendWelcome($email);
return $this->app->json(['status' => 'success', 'message' => 'User registered']);
}
}
І тест з моками:
use PHPUnit\Framework\TestCase;
class UserControllerDICTest extends TestCase {
public function testValidEmailSavesAndSendsEmail() {
$mockDb = $this->createMock(flight\database\PdoWrapper::class);
$mockDb->method('runQuery')->willReturn(true);
$mockMailer = new class {
public $sentEmail = null;
public function sendWelcome($email) { $this->sentEmail = $email; return true; }
};
$app = new flight\Engine();
$app->request()->data->email = 'test@example.com';
$controller = new UserController($app, $mockDb, $mockMailer);
$controller->register();
$response = $app->response()->getBody();
$result = json_decode($response, true);
$this->assertEquals('success', $result['status']);
$this->assertEquals('User registered', $result['message']);
$this->assertEquals('test@example.com', $mockMailer->sentEmail);
}
}
Розширене використання
- Мокування: Використовуйте вбудовані мокі PHPUnit або анонімні класи для заміни залежностей.
- Тестування контролерів безпосередньо: Створюйте екземпляри контролерів з новим
Engine
та мокайте залежності. - Уникайте надмірного мокування: Дозволяйте реальній логіці виконуватися, де можливо; мокайте лише зовнішні сервіси.
Дивіться також
- Unit Testing Guide - Комплексний посібник з найкращих практик тестування модулів.
- Dependency Injection Container - Як використовувати DIC для керування залежностями та покращення тестованості.
- Extending - Як додавати власні помічники або перевизначати основні класи.
- PDO Wrapper - Спрощує взаємодію з базами даних і легше мокувати в тестах.
- Requests - Обробка HTTP-запитів у Flight.
- Responses - Надсилання відповідей користувачам.
- Unit Testing and SOLID Principles - Дізнайтеся, як принципи SOLID можуть покращити ваші тести модулів.
Вирішення проблем
- Уникайте використання глобального стану (
Flight::set()
,$_SESSION
тощо) у вашому коді та тестах. - Якщо ваші тести повільні, ви, можливо, пишете інтеграційні тести — мокайте зовнішні сервіси, щоб тримати тести модулів швидкими.
- Якщо налаштування тестів складне, розгляньте рефакторинг вашого коду для використання ін'єкції залежностей.
Журнал змін
- v3.15.0 - Додано приклади для ін'єкції залежностей та мокування.