Python: Продвинутое тестирование
Теория: Мокинг
В тестировании очень популярен мокинг. Технически он похож на стабинг, и из-за этого их часто путают. Но все же они служат разным целям и используются в разных ситуациях. В этом уроке мы разберемся, что такое мокинг и зачем он нужен.
До этого момента мы рассматривали побочные эффекты как помеху тестирования нашей логики. Для их изоляции использовались либо стабы, либо прямое выключение логики в тестовой среде. После этого можно было спокойно проверять правильность работы функции.
В некоторых ситуациях требуется кое-что другое. Иногда нам нужно не получить результат работы функции, а проверить, выполняет ли она нужное нам действие — например, шлет правильный HTTP-запрос с правильными параметрами.
Для этого понадобятся моки. Они проверяют, как выполняется код.
HTTP
Начнем с такого фрагмента кода:
Отслеживание выполнения какого-то действия — это и есть мокинг. Мок проверяет, что какой-то код выполнился определенным образом. Это может быть вызов функции, HTTP-запрос и тому подобное.
У мока две задачи:
- Убедиться в том, что событие произошло — например, функция передала данные
- Отследить, каким конкретно образом оно произошло — функция передала конкретные данные
Что дает нам такая проверка? В этом случае не очень много. Да, мы убеждаемся, что вызов был, но само по себе это еще ни о чем не говорит. В чем же тогда польза моков?
Представьте, что мы бы разрабатывали библиотеку PyGithub — ту самую, что выполняет запросы к GitHub API. Вся суть этой библиотеки в том, чтобы выполнить правильные запросы с правильными параметрами. Поэтому там нужно обязательно проверять выполнение запросов с указанием точных URL-адресов. Только в таком случае можно быть уверенными, что она выполняет верные запросы.
В этом ключевое отличие мока от стаба:
- Стаб устраняет побочный эффект, чтобы не мешать проверке результата работы кода — например, возврату данных из функции
- Мок фокусируется на том, как конкретно работает код, что он делает внутри
При этом чисто технически мок и стаб создаются почти одинаково, за исключением того, что на мок вешают ожидания, проверяющие вызовы. Из-за этого моками часто называют стабы. Для себя всегда пытайтесь понять, о чем идет речь. Это важно, потому что от этого зависит фокус тестов.
Функции
Моки довольно часто используют с функциями (методами). К примеру, они могут проверять:
- Вызвана ли функция, сколько раз ее вызвали
- Какие аргументы переданы в функцию, сколько всего аргументов
- Что именно вернула функция
Предположим, что мы хотим протестировать функцию for_each(). Она вызывает колбек для каждого элемента коллекции:
Эта функция ничего не возвращает, поэтому напрямую ее не протестировать.
Можно попробовать сделать это с помощью моков. Проверим, что она вызывает переданный колбек и передает туда нужные значения. Сделаем это с помощью модуля mock, который входит в стандартную библиотеку Python как часть unittest:
С помощью моков мы проверили, что функция была вызвана ровно три раза, и ей передавался новый элемент коллекции последовательно для каждого вызова.
Можно сказать, что этот тест действительно проверяет работоспособность функции for_each(). Но можно сделать это проще, без мока и без завязки на внутреннее поведение. Для этого достаточно использовать замыкание:
Объекты
Кроме использования моков для тестирования функций, они также могут использоваться для тестирования объектов.
В тестировании объектов моки могут использоваться для имитации поведения объектов, от которых зависит тестируемый объект. Например, если объект A зависит от объекта B, то можно создать мок-объект для объекта B и использовать его в тестах объекта A. Это позволит тестировать объект A, не затрагивая объект B и его зависимости.
Еще он умеет оборачивать существующую реализацию:
Преимущества и недостатки
Существуют ситуации, в которых моки нужны, но все таки в большинстве ситуаций их нужно избегать. Моки слишком много знают о том, как работает код. Любой тест с моками из черного ящика превращается в прозрачный ящик.
Повсеместное использование моков приводит к двум вещам:
- После рефакторинга приходится переписывать тесты, даже если код работает правильно. Так происходит, потому что тесты завязаны на то, как конкретно работает код
- Код может перестать работать. При этом тесты будут проходить, потому что они сфокусированы не на результатах работы кода, а на его устройстве внутри
Там, где возможно использование реального кода — используйте реальный. Там, где возможно убедиться в работе кода без моков — делайте это без моков.
Излишний мокинг повышает стоимость поддержки тестов и делает их бесполезными. Идеальные тесты – тесты методом черного ящика.




