При тестировании кода, использующего какие-то внешние компоненты, часто применяют подход mock-объектов. (1) Это такие объекты, которые имеют тот же интерфейс, что и используемые компоненты, но их поведение полностью задаётся в тесте, и их использование позволяет избежать поднятия полной инфраструктуры, необходимой приложению для запуска. Что ещё более важно, можно легко и непринуждённо проконтролировать, что код вызывал те или иные методы у mock-объекта с теми или иными аргументами.

Тестовые «двойники» или Mock-объекты в PHPUnit

Одной из мощнейших возможностей phpunit являются «двойники» для тестов. Очень часто в нашем коде функция одного класса вызывает функцию другого класса. В этом случае, мы имеем зависимость в этих двух классах. В частности, вызывающий класс имеет зависимость от вызываемого класса. Но как мы уже знаем из первой части, юнит-тест должен тестировать функциональную единицу, в этом случае, он должен проверить только в вызывающую функцию.

Чтобы решить эту проблему, мы можем использовать «двойника» для подмены вызываемого класса. Так как двойник может быть настроен возвращать предопределенные результаты, мы можем сосредоточиться на тестировании в вызывающей функции.

Типы тестовых «двойников»

Тестовый «двойник» (англ. test doubles) — это общий термин для объектов, которые мы используем, чтобы заменить реальные объекты. На наш взгляд, это очень полезно для классификации тестовых «двойников» по их назначению. Это не только облегчит нам понимание описанного тестируемого случая, но и сделает наш код более дружелюбным и читаемым для других.

Опираясь на пост Мартина Фаулера, есть пять типов тестовых «двойников»:

  • Dummy (манекены/заглушки) предназначены для работы в обход, но фактически никогда не используются. Обычно они просто используются чтобы заполнять список параметров.

  • Fake (фальшивки) действительно имеют работающие реализации, но обычно имеют некоторое упрощение, которое делает их непригодными для боевого применения.

  • Stubs (заглушки/огрызки) обеспечивают консервированные ответы на вызовы, сделанные во время теста, как правило, не отвечают вообще ни на что извне, кроме того, что запрограммировано для теста.

  • Spies (шпионы) являются заглушками (stub-ом), которые, кроме того, регистрируют определенную информацию, основываясь на том, как они назывались. Одним из примеров этого может быть почтовый сервис, который записывает, сколько сообщений было отправлено.

  • Mocks (подражатели/притворщики) запрограммированы с ожиданиями, которые формируют спецификацию вызова, который они ожидали получить. Они могут выбросить исключение, если они были вызваны не так, как они ожидали, и выполняют проверки, чтобы убедиться, что они получили все вызовы, которые они ожидали.

Как создать тестового «двойника»

В PhpUnit метод getMockBuilder может быть использован для создания любых подобных объектов, определяемых пользователем. В сочетании с настраиваемым интерфейсом, мы можем использовать его, чтобы создать, по сути, все пять типов тестовых «двойников».

Добавление «двойника» (mock) unit-тест

Не имеет смысла использовать «двойника» для тестов нашего калькулятора, поскольку в настоящее время калькулятор класса не имеет зависимостей от других классов. Однако, чтобы продемонстрировать, как использовать «двойников» с помощью PhpUnit, мы создадим stub для класса Calculator и протестируем его.

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

Создаем простейший mock:

class CalculatorTest extends PHPUnit_Framework_TestCase {
    public function test_process() {
        $mock = $this->getMock('Calculator');

        // проверяем, что в $mock находится экземпляр класса Calculator
        $this->assertInstanceOf('Calculator', $mock);
    }
}

(звезда) в данном примере мы “замочили класс“, создав на основе реального класса новый, с нужным поведением

Давайте теперь добавим тест-метод под названием testWithStub в наш существующий класс:

public function testWithStub()
{
    // Create a stub for the Calculator class.
    $calculator = $this->getMockBuilder('Calculator')
                       ->getMock();

    // Configure the stub.
    $calculator->expects($this->any())
               ->method('add')
               ->will($this->returnValue(6));

    $this->assertEquals(6, $calculator->add(100,100));
}
  • getMockBuilder() метод создает заглушку (stub), похожую на объект нашего класса Calculator.

  • getMock() возвращает объект этой заглушки.

  • expects() говорит, что stub будет вызываться любое число раз.

  • method() определяет, какой метод будет вызван.

  • will() настраивает возвращаемое значение в stub

(звезда) не стоит сильно увлекаться Mock объектами в каких-либо целях, отличных от изоляции тестируемого класса от внешних источников данных. Иначе при любом, даже незначительном, изменении исходного кода вам скорее всего понадобится также править и сами тесты. А довольно значительный рефакторинг может привести к тому, что вам придется вообще полностью их переписывать. (3)


Источники:

(1) https://habr.com/ru/post/136466/?mobile=no

(2) https://phpprofi.ru/blogs/post/41

(3) https://habr.com/ru/post/140217/

Tags

Нет комментариев

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

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.