Если у вас возникают вопросы: Что такое Hard Dependencies и почему это плохо? Вы можете прочитать в статье https://wp-punk.com/dependency-injection/. Внедрения зависимостей (Dependency Injection) один из основных принципов, которых нужно придерживаться для написание качественного кода.
Но иногда мы сталкиваемся с проблемами, когда избежать зависимостей невозможно или же это занимает слишком много времени.
Сложность заключается в том, что зависимости мы не должны тестировать, мы должны создать для них заглушки.
Установка Mockery
Нам понадобиться библиотеку Mockery:
composer require mockery/mockery --dev
Фикстуры для Mockery
Что такое фикстуры можно прочитать https://wp-punk.com/modulnoe-testirovanieunit-tests-s-pomoshhyu-phpunit/
Для работы библиотеки Mockery нам нужно изменить фикстуры:
use PHPUnitFrameworkTestCase;
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 BirdDuck();
return $duck->say();
}
Создаем заглушку для класса BirdDuck:
$mock = Mockery::mock( 'BirdDuck' );
$mock->shouldReceive('say')
->once()
->andReturn( 'krya' );
Заглушку мы не можем передать в тестируемую функцию исходя из задачи. Но мы можем ее переопределить с помощью overload в библиотеке Mockery:
...
public function test_bird_say() {
$mock = Mockery::mock( 'overload:BirdDuck' );
$mock->shouldReceive('say')
->once()
->andReturn( 'krya' );
$this->assertSame( 'krya', bird_say() );
}
...
Тест работает, но теперь все объекты BirdDuck в тестовом классе, которые идут после тестового метода будут иметь такую же заглушку. Это очень плохо так как следующий пример вызывает ошибку из-за того, что BirdDuck в тесте test_bird_say_2 уже объявлен. Этого быть не должно.
...
public function test_bird_say() {
$mock = Mockery::mock( 'overload:BirdDuck' );
$mock->shouldReceive('say')
->once()
->andReturn( 'krya' );
$this->assertSame( 'krya', bird_say() );
}
public function test_bird_say_2() {
$mock = Mockery::mock( 'overload:BirdDuck' );
$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:BirdDuck' );
$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
- Добавить аннотации к тестируемому методу