При работе с моками создаётся объект, который доступен извне так же, как настоящий, но разработчик полностью контролирует его поведение. Именно этот объект и называется mock.
Note
В языке Go мок-тестирование особенно удобно благодаря концепции интерфейсов. По сути, всё, что нужно для создания объекта-заглушки, — это удовлетворить интерфейсу реального объекта. В других ООП-языках — вроде Python или Java — мок-тестирование также существует, однако может быть сложнее в случае сложной иерархии наследования.
Это бывает полезно, когда:
- нужно протестировать только работу бизнес-логики;
- процессы занимают много времени и его можно сэкономить при тестировании;
- нельзя или нежелательно при тестировании выполнять какую-то операцию, например отправку email или уведомлений;
- невозможно развернуть копию БД или она представляет собой чёрный ящик;
- сложно тестировать необходимые состояния во внешних источниках данных и проще установить нужные граничные условия на моках.
Проще всего понять принцип работы с моками на примере.
Предположим, есть БД, которую нельзя использовать для тестирования, но надо проверить, правильно ли работает написанный код для работы с ней. Возьмём простейший случай: пакет для работы с БД имеет тип DB
и метод для проверки существования пользователя по его email. Метод UserExists
возвращает true
, если пользователь с указанным адресом существует, и false
, если нет.
В прошлых темах рассматривалось понятие интерфейсного типа, в котором описывается только поведение (методы) какого-то объекта. При этом структура и его внутренняя реализация не имеют значения — можно описать набор методов для работы с БД в виде интерфейса.
В продакшене будем использовать тип, который подключается к базе данных и отправляет запросы, а для тестирования создадим тип с такими же методами, при вызове которых сможем сравнить результаты с эталонными значениями.
Здесь определены интерфейсный тип DBStorage
и функция NewUser
, в которой происходит проверка на существование пользователя с таким же почтовым ящиком. В продакшене эту функцию будем вызывать для переменной типа DB
, а сейчас напишем для неё тест.
Если есть много вариантов для проверки, то для тестирования лучше использовать таблицы (table-driven tests) с входящими данными и ожидаемыми результатами:
В тестовой функции TestNewUser
проверяем, какой результат возвращает функция NewUser
в зависимости от того, существует пользователь с таким email или нет. Таким образом можно протестировать поведение своей функции без обращения к реальной базе данных. В примере на каждую итерацию создаём мок и при необходимости добавляем туда email, чтобы проверить различные варианты.
Моки можно использовать не только для подмены операций, но и для получения детальной информации, например количества вызовов функции, контроля параметров и т. д. Для этого достаточно добавить нужные поля в структуру заглушки и изменять их при каждом вызове интерфейсного метода:
Итак, мы разобрались с общими принципами работы моков, которые дают дополнительные возможности для тестирования ПО. В простых случаях можно самостоятельно создавать подобные тесты, а на практике лучше использовать готовые библиотеки для тестирования с использованием моков:
Дополнительные материалы
GODEVPL10
📂 Go | Последнее изменение: 01.09.2024 14:21