Тестовые дублёры при модульном(Unit) тестировании.

Тестовые дублёры при модульном(Unit) тестировании.

KarmanovichDev

Сутью модульных тестов является изолированное тестирование объекта(иначе это не модульные тесты). Нам не нужно беспокоиться о создании и работе зависимостей тестируемого объекта, что даёт нам такие преимущества как:

  • Скорость написания/выполнения тестов
  • Простота настройки тестов. Нам не нужно предзаполнять базу данных или настраивать(patch(ить)) то, что вернётся после запроса в сеть.
  • Детерминированность теста - результат работы тестируемого объекта не должен меняться от запуска к запуску тестов. Тест должен зависеть не от внешнего изменяющегося состояния, а от определенных данных в тесте.
  • Низкая сложность в понимании/изменении отдельного теста для разработчика.
  • Скорость отлова багов(При изоляции зависимостей ошибка может случиться только в тестируемом объекте).


Как сделать тест изолированным от зависимостей тестируемого объекта? Всё просто, нужно использовать тестовые дублёры.

Тестовые дублёры - Заглушки с помощью которых происходит замена реальных зависимостей тестируемого объекта. Имеют упрощённую реализацию подменяемого объекта либо вообще не имеют её возвращая заранее подготовленный объект.

В основном тестовые дублёры делятся на три группы:

  1. Имитируют поведение(Fake, Stub)
  2. Проверяют взаимодействие(Mock, Spy)
  3. Не имитируют поведение и не проверяют взаимодействие(Dummy)

Иногда для простоты их все называют как Mock(и). Хоть они и похожи, каждый по отдельности имеет за собой чёткую цель применения.


Dummy(пустышка/глупый объект) - Самый простой тестовый дублёр, скорее всего кроме как передать его аргументом в тестируемый объект в тесте где он не используется, будет единственным правильным использованием.

Как видите зависимость logger требуется лишь в исключительной ситуации(при PriceDoesNotExist). Этот тест тестирует happy path сценарий, поэтому в рамках него взаимодействие с logger не требуется. Но если вдруг кто-то начнёт использовать logger в happy path сценарии, то этот тест закончится с ошибкой, так как у нашего тестового дублёра нет никакой реализации. Такое поведение более явное но делает ваш тест более хрупким. Вам самим нужно решить как балансировать между этими характеристиками.


В прошлом тесте вы могли заметить FakePriceRepository, поэтому давайте обсудим этот дублёр.

Fake - Тестовый дублёр, который имитирует поведение настоящего объекта и имеет функциональную реализацию(упрощённую относительно реального объекта).

Это лишь часть полной реализации, но замечу, что полная реализация не обязательна, главное реализовать требуемые для теста(ов) методы.

Полную реализацию такого репозитория можно внедрять в любой тест где ожидается AbstractUserRepository и быть уверенными что его поведение не будет отличаться от настоящего объекта.

Чем сложней реализация такого Fake объекта, тем больше смысла в его тестировании. Например, вместо FakePriceRepository мы могли бы сделать InMemoryRepository, покрыть его тестами, и использовать как в тестах, так и например при локальном(боевом) запуске вашего проекта. Репозиторий будет хранить данные прямо в оперативной памяти складывая их в состояние класса репозитория.


Stub - Тестовый дублёр, ответственность которого схожа с Fake, за исключением того, что он не имеет функциональной реализации. Его задача в большинстве случаев просто отдать заготовленный ответ.

Старайтесь делать свои тестовые дублёры так, что бы они реагировали на те аргументы, что были переданы вами в тесте. Будет странно если вы вызовете .get_by_id(id=2) и получите обратно объект Price(id=1, value=300)

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


Mock - Тестовый дублёр в возможности которого должна входить такая функциональность как:

  • Установка требуемых ответов при взаимодействии с интерфейсом дублёра. Может быть как один статичный ответ, так и определённое кол-во последовательных(если вызвать больше раз чем ожидалось, возбудится ошибка, что позволяет писать более гибкие тесты на поведение).
  • Проверка того, как код тестируемого объекта взаимодействовал с дублёром: сколько раз были вызваны методы, с какими аргументами и т. п.


Spy - Тестовый дублёр, ответственность которого схожа с Mock за исключением того, что у него присутствует функциональная реализация. Это может потребоваться для более сложных установок возвращаемых данных и обработки взаимодействия с дублёром.

Как и в Mock у Spy есть возможность задавать ответы, так и сохранять любую информацию в момент использования тестового дублёра.

Это лишь один из возможных примеров, с чего может начаться создание Spy. На самом деле в большинстве случаев будет достаточно Mock с реализацией через pytest или unittest.


Эта статья даёт базовое понимание, что такое тестовые дублёры. В скором времени я напишу статью(и) или запишу стрим на темы:

  • Как выбрать тестовый дублёр в конкретном случае.
  • Что нужно понимать отдавая предпочтение Mock(ам) или Fake(ам).
  • Коротко затронем тему(потому что будет отдельная статья), что такое тесты на поведение и тесты на работу.

Report Page