Как тестировать Hard Dependencies в PHPUnit с помощью Mockery

3 мин. 5 сек.
328
10

Если у вас возникают вопросы: Что такое 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 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
  • Добавить аннотации к тестируемому методу

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *