Как тестировать Hard Dependencies в PHPUnit с помощью Mockery
Если у вас возникают вопросы: Что такое Hard Dependencies и почему это плохо? Вы можете прочитать в статье «Dependency Injection the best design pattern«.
Внедрения зависимостей (Dependency Injection) один из основных принципов, которых нужно придерживаться для написание качественного кода.
Но иногда мы сталкиваемся с проблемами, когда избежать зависимостей невозможно или же это занимает слишком много времени.
Сложность заключается в том, что зависимости мы не должны тестировать, мы должны создать для них заглушки.
Установка Mockery
Нам понадобиться библиотеку Mockery:
composer require mockery/mockery --dev
Фикстуры для Mockery
Что такое фикстуры можно прочитать в статье «Модульное тестирование (Unit tests) с помощью PHPUnit«.
Для работы библиотеки Mockery нам нужно изменить фикстуры:
use PHPUnit\Framework\TestCase;
class Test_Bird extends TestCase {
/**
* Setup test
*/
public function setUp(): void {
Mockery::resetContainer();
parent::setUp();
}
/**
* End test
*/
public function tearDown(): void {
Mockery::close();
parent::tearDown();
}
}
Первый тест Hard Dependency
Пример тестируемого класса:
function bird_say(): string {
$duck = new \Bird\Duck();
return $duck->say();
}
Создаем заглушку для класса \Bird\Duck:
$mock = Mockery::mock( 'Bird\Duck' );
$mock->shouldReceive('say')
->once()
->andReturn( 'krya' );
Заглушку мы не можем передать в тестируемую функцию исходя из задачи. Но мы можем ее переопределить с помощью overload в библиотеке Mockery:
public function test_bird_say() {
$mock = Mockery::mock( 'overload:Bird\Duck' );
$mock->shouldReceive('say')
->once()
->andReturn( 'krya' );
$this->assertSame( 'krya', bird_say() );
}
Тест работает, но теперь все объекты Bird\Duck в тестовом классе, которые идут после тестового метода будут иметь такую же заглушку. Это очень плохо так как следующий пример вызывает ошибку из-за того, что Bird\Duck в тесте test_bird_say_2 уже объявлен. Этого быть не должно.
public function test_bird_say() {
$mock = Mockery::mock( 'overload:Bird\Duck' );
$mock->shouldReceive('say')
->once()
->andReturn( 'krya' );
$this->assertSame( 'krya', bird_say() );
}
public function test_bird_say_2() {
$mock = Mockery::mock( 'overload:Bird\Duck' );
$mock->shouldReceive('say')
->once()
->andReturn( 'no-krya' );
$this->assertSame( 'no-krya', bird_say() );
}
Первый хороший тест Hard Dependency
Для того, чтобы избежать зависимости между тестовыми методами, нужно добавить аннотации к каждому тестовому методу, который использует overload:
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_bird_say() {
$mock = Mockery::mock( 'overload:Bird\Duck' );
$mock->shouldReceive('say')
->once()
->andReturn( 'krya' );
$this->assertSame( 'krya', bird_say() );
}
/**
* @runInSeparateProcess
* @preserveGlobalState disabled
*/
public function test_bird_say_2() {
// ...
}
- @runInSeparateProcess — Указывает, что тест должен выполняться в отдельном процессе PHP;
- @preserveGlobalState disabled — нужно запретить сохранять глобальное состояние.
Теперь все работает как надо!
Краткий обзор
Для тестирования Hard Dependencies нам нужно сделать следующее:
- Установить Mockery
- Обновить фикстуры в тестовом классе
- При создании заглушки добавить overload
- Добавить аннотации к тестируемому методу
Источник: WP Punk.