Что такое моки в тестировании
Модуль Mock: макеты-пустышки в тестировании
Mock на английском значит «имитация», «подделка». Модуль с таким названием помогает сильно упростить тесты модулей на Питоне.
Принцип его работы простой: если нужно тестировать функцию, то всё, что не относится к ней самой (например, чтение с диска или из сети), можно подменить макетами-пустышками. При этом тестируемые функции не нужно адаптировать для тестов: Mock подменяет объекты в других модулях, даже если код не принимает их в виде параметров. То есть, тестировать можно вообще без адаптации под тесты.
Такое поведение — уже не надувные ракетные установки, а целая надувная земля, вокруг которой могут летать испытуемые ракеты и самолёты.
На Хабре пакет Mock упоминали только в одном комментарии. В принципе, подменять объекты в других модулях, что называется monkey patch, нам иногда в работе приходится. Но в отличие от ручных патчей, Mock умеет делать очень сложные подстановки, пачками и цепочками вызова, а также убраться за собой, не оставляя побочных эффектов.
Пакет занимает меньше 1 мегабайта и устанавливается очень просто:
$ pip install mock
или
$ easy_install mock
И теперь им можно пользоваться.
Подмена функции
Скажем, наша функция считает что-то, причём очень долго:
Пример надуманный и примитивный, но что-то подобное может встретиться: внутри делаются большие вычисления, которые не хотелось бы повторять в тесте. И в данном примере хочется ввести строку покороче (чтобы число повторений в permutations, len(name), было меньше), но это запрещено. Можно было бы разбить функцию на две, вынести вызов permutations наружу, и в функцию передавать её вывод, но можно сделать по-другому.
Вместо переписывания кода, мы можем просто «пропатчить» функцию permutations на время вызова, задать только определённый вывод и вызвать какой-то код:
Заметьте: print вызван вместо 42! / (42 — 10)! раз всего 3, то есть цикл пробежался по xrange(3), который мы подставили.
Кроме того, после выхода из контекста with функция itertools.permutations вернулась в своё нормальное состояние.
Отладочная информация
Допустим, нужно проверить, что происходит с объектом, который мы передали функции. В той ли последовательности, с теми ли параметрами вызываются методы, к тем ли атрибутам обращаются. Для нужно просто запустить в неё объект Mock, который запишет всё, что происходит.
Похожий пример из жизни: когда шли аэродинамические испытания Бурана, весь корабль не продували в трубе (таких не бывает) и не запускали в атмосферу. В воздухе летал специальный прототип БТС-002, а для отработок посадки использовался переоборудованный Ту-154 в обвесе, повторяющем аэродинамику Бурана.
У объекта Mock есть несколько атрибутов с информацией о вызовах:
В нашем примере выше можно убедиться, что функция real() правильно вызывает permutations. Чтобы ещё точнее проверить, например, в автоматических тестах, можно вызвать один из методов assert_*:
Синтаксический сахар
Для юнит-тестов в Джанго пригодится то, что patch работает как декоратор:
Макеты атрибутов и цепочек вызова
Иногда в Джанго нужно делать что-то с файлами, и лучше обойтись без сохранения их на диск или другое хранилище. Обычным путём мы бы наследовали от класса File и перезаписали бы некоторые свойства, что было бы громоздко. Но в Mock можно описать сразу и атрибуты, и цепочки вызова:
Вот сколько тестового кода было сэкономлено. Кстати, передавая объект как аргумент или ожидая объект из функции, полезно дать им имена, чтобы отличать:
Как же эти цепочки атрибутов работают?
Как видите, обращение к атрибуту выдаёт ещё один экземпляр класса Mock, а повторное обращение к тому же атрибуту — снова тот же экземпляр. Атрибут может быть чем угодно, в том числе и функцией. Наконец, любой макет можно вызвать (скажем, вместо класса):
Это будет другой экземпляр, но если вызвать ещё раз, экземпляр будет тем же самым. Так мы можем назначить этим объектам некоторые свойства, после чего передать этот объект в тестируемый код, и они там будут считаны.
Если мы назначим атрибуту значение, то никаких сюрпризов: при следующем обращении получим именно это значение:
Полезно прочесть в документации класса, какие атрибуты у него родные, чтобы не случалось коллизии названий.
Как ограничить гибкость макета
Как видите, можно обращаться к любому атрибуту макета, не вызывая ошибки AttributeError. У этого удобства есть обратная сторона: что если мы поменяем API, например, переименуем метод, а функция, работающая с нашим классом, будет обращаться к прежнему методу? Код на самом деле не работает, а тест выполняется без ошибок. Для этого можно задать спецификацию объекта в параметре spec (либо классу Mock, либо patch), и макет будет вызывать ошибку при обращении к несуществующим свойствам:
Теперь если мы переименуем model или shoot у танчика, но забудем исправить tank_sugar, тест не выполнится.
Как сделать макет умнее
Хорошо, допустим, Mock умеет заменять нужные объекты на ненужные и подменять вывод. А можно ли подменить функцию на что-то более сложное, чем значение (return_value)? Есть 2 пути:
Кстати, не нужно писать в simple_function контрольный вывод, потому что, как сказано выше, в конце кода в объекте mock_class можно считать method_calls.
Подмена встроенных функций
Сам по себе Mock не может заменить встроенные в язык функции (len, iter), но может сделать макет с нужными «волшебными» функциями. Например, вот мы делаем макет файла:
Для множества подобных случаев, когда требуется эмулировать стандартный объект (список, файл, число), есть класс MagicMock с набором значений, пригодных для тестов.
Где Mock не работает
Сам автор модуля, Майкл Фурд, говорит, что принцип, где нужны макеты, а где нет, простой: если с макетами тестировать проще, их надо использовать, а если с ними труднее, надо отказаться.
Бывает так, что нужно тестировать связку двух модулей, нового и старого, и в связке много перекрёстных вызовов. Нужно внимательно смотреть: если мы постепенно начинаем переписывать поведение целого модуля, пора остановиться — код теста жёстко сввязан с кодом модуля, и при каждом изменении в рабочем коде придётся менять и тесты. Кроме того не стоит пытаться написать целый модуль-макет вместо старого.
По моему личному опыту, Mock может конфликтовать с отладчиками, например, в PuDB случалась бесконечная рекурсия. IPDB работал нормально, поэтому тесты проекта мы выполняли с IPDB, а просто код — на PuDB.
Выводы
Макеты в Mock можно подставлять всюду и как угодно. Ваш код не придётся подстраивать под тестирование, что значит быстрее разработка, и, возможно, быстрее прототипирование.
Можно выбросить из тестов всё лишнее, всё занимающее время и ресурсы, оставив работать только тот код, который нужно проверить.
Настройки макетов где нужно жёсткие (spec), где нужно гибкие, и патчи не оставляют за собой следов.
Моки не кусаются! Осваиваем мокинг с React Testing Library
Они предназначены помочь вам создавать более простые и надежные тесты. В этой серии статей я продемонстрирую вам паттерны, на которые я опираюсь при написании моков (mocks или “заглушек”) компонентов React.
Прежде чем мы рассмотрим, как они используются, давайте сначала вспомним, что такое моки и почему вы вообще можете захотеть их использовать.
Что такое мок?
В мире JavaScript термин mock очень широко применяется для обозначения любой имитированной реализации или тестового двойника (test double). Имитированные реализации — это просто значения, которые заменяют другие в вашем производственном коде во время выполнения тестов. Они примеряют на себя интерфейс заменяемого объекта, так что остальная часть вашего кода работает так, как если бы никакой замены не было.
Есть несколько разных причин, по которым вам может это понадобиться; мы рассмотрим их на примерах.
Если вам интересно узнать больше о имитированных реализациях, почитайте книгу Мартина Фаулера «Моки — не заглушки».
Jest и мокинг
В книге Mastering React Test-Driven Development я использую импорт именованных модулей ES6 для создания тестовых двойников. Такой подход дает немного больше гибкости, но выглядит слегка более хакерским.
На странице Jest про jest.mock говорится, что моки гарантируют, что ваши тесты будут быстрыми и не хрупкими.
Хотя это правда, это не основная причина, по которой я использую моки.
Я использую моки, потому что они помогают мне поддерживать мои тесты независимыми друг от друга.
Чтобы понять, почему это так, давайте рассмотрим пример.
Почему моки?
Поддерживать тесты в рабочем состоянии становится сложнее. Каждый новый тест имеет более сложный сетап, и набор тестов начинает поедать все больше вашего времени — бремя, которое теперь необходимо поддерживать.
Это распространенная проблема, и я все время вижу ее в кодовых базах React. Наборы тестов, в которых даже простейшее изменение результирует в сбоях многих тестов.
Но что, если рендеринг PostContent имеет побочные эффекты?
Набор тестов в BlogPage.test.js должен знать о побочных эффектах и быть готовым обрабатывать их. Например, он должен будет держать наготове fetch ответ.
Зависимость, которой мы пытались избежать путем разделения наших тестовых наборов, все еще существует.
Организация нашего тестирования, безусловно, стала лучше, но в конечном итоге ничего, что бы сделало наши тесты менее хрупки, не произошло.
Так ли это необходимо?
Кстати, пара слов о переходе из области сквозных тестов в область модульных тестов.
Наличие тестовых двойников — ключевой показатель того, что вы пишете модульные тесты.
Многие опытные тестировщики сразу же начинают новые проекты с модульных тестов (и моков), потому что они знают, что по мере роста их кодовой базы они будут сталкиваться с проблемой нестабильности тестов.
Модульные тесты обычно намного меньше, чем сквозные. Они могут быть настолько малы, что часто занимают не больше трех-четырех строк кода. Это делает их отличными кандидатами для таких практик социального кодирования, как парное и ансамблевое программирование.
Даже когда мы проводим модульное тестирование, тестовые двойники не всегда необходимы — это просто еще один инструмент в вашем наборе, который вы должны знать, когда и как применять.
Когда использовать mocks в юнит-тестировании
Эта статья является переводом материала «When to Mock».
Использование моков в модульном тестировании является спорной темой. Автор оригинала заметил, что на протяжении всей своей карьеры в программировании он сначала перешел от моков почти над каждой зависимостью к политике «без моков», а затем к «только моки для внешних зависимостей».
Ни одна из этих практик не является достаточно хорошей. В этой статье Владимир Хориков покажет, какие зависимости следует мокать, а какие использовать как есть в тестах.
Что такое mock (мок, от англ. «пародия», «имитация»)?
Прежде чем перейти к теме того, когда использовать моки, давайте обсудим, что такое мок. Люди часто используют термины тестовый двойник (test double) и мок (mock) как синонимы, но технически это не так:
Мок – это лишь один из видов таких зависимостей.
Согласно Жерару Месарошу, существует 5 видов тестовых двойников:
Такое разнообразие может показаться пугающим, но на самом деле все они могут быть сгруппированы всего в два типа: моки и стабы.
Разница между этими двумя типами сводится к следующему:
Моки помогают имитировать и изучать исходящие (outcoming) взаимодействия. То есть вызовы, совершаемые тестируемой системой (SUT) к ее зависимостям для изменения их состояния.
Стабы помогают имитировать входящие (incoming) взаимодействия. То есть вызовы, совершаемые SUT к ее зависимостям для получения входных данных.
Извлечение данных из БД является входящим (incoming) взаимодействием — оно не приводит к побочному эффекту. Соответствующий тестовый двойник является стабом.
Все остальные различия между пятью типами тестовых двойников являются незначительными деталями реализации:
Spies (шпионы) выполняют ту же роль, что и моки. Отличие в том, что spies пишутся вручную, а моки создаются с помощью готовых инструментов. Иногда spies называют рукописными моками.
С другой стороны, разница между стабами, dummy (пустышками) и фейками заключается в том, насколько они умны:
Стаб посложнее. Это полноценная зависимость, которую вы настраиваете для возврата разных значений для разных сценариев.
Обратите внимание на разницу между моками и стабами (помимо исходящих и входящих взаимодействий). Моки помогают эмулировать и изучать взаимодействия между SUT и его зависимостями, в то время как стабы помогают только эмулировать эти взаимодействия. Это важное различие.
Мок-как-инструмент vs. мок-как-тестовый-двойник
Но у термина мок есть и другое значение. Вы также можете ссылаться на классы из библиотек (для создания моков) как на моки. Эти классы помогают вам создавать настоящие моки, но сами по себе они не являются моками как таковыми:
Важно не смешивать мок-как-инструмент с мок-как-тестовый-двойник, потому что вы можете использовать мок-как-инструмент для создания обоих типов тестовых двойников: моков и стабов.
Не проверяйте взаимодействия со стабами
Как уже упоминал выше, моки помогают эмулировать и изучать исходящие взаимодействия между SUT и его зависимостями, в то время как стабы помогают только эмулировать входящие взаимодействия, а не изучать их.
В приведенных выше примерах проверка
В то же время вызов GetNumberOfUsers() вообще не является результатом. Это внутренняя деталь реализации, касающаяся того, как SUT собирает данные, необходимые для создания отчета. Следовательно, проверка этого вызова приведет к уязвимости теста. Неважно, как SUT генерирует конечный результат, если этот результат правильный.
Вот пример такого хрупкого теста:
Такая практика проверки того, что не является частью конечного результата, также называется чрезмерной спецификацией.
Совместное использование моков и стабов
Иногда нужно создать тестовый двойник, который проявляет свойства как мока, так и стаба:
Этот тест использует storeMock для двух целей: он возвращает шаблонный ответ и проверяет вызов метода, сделанный SUT.
Однако обратите внимание, что это два разных метода: тест устанавливает ответ от HasEnoughInventory(), но затем проверяет вызов RemoveInventory(). Таким образом, здесь не нарушается правило не проверять взаимодействия со стабами.
Mocks vs. stubs и commands vs. queries
Понятие моков и стабов связано с принципом command-query separation (CQS). Принцип CQS гласит, что каждый метод должен быть либо командой, либо запросом, но не обоими:
Другими словами, задавая вопрос, вы не должны менять ответ. Код, который поддерживает такое четкое разделение, становится легче для чтения. Тестовые двойники, заменяющие команды, становятся моками. Аналогично, тестовые двойники, заменяющие запросы, являются стабами:
Посмотрите еще раз на два теста из предыдущих примеров:
Когда мокать?
Разобравшись со всеми этими определениями, давайте поговорим о том, когда вам следует использовать моки.
Очевидно, что вы не хотите мокать саму тестируемую систему (SUT), поэтому вопрос «Когда мокать?» сводится к следующему: «Какие типы зависимостей вы должны заменять на моки, а какие использовать в тестах?»
Вот все типы зависимостей модульного тестирования, которые автор оригинала перечислил в своей предыдущей статье:
Совместная зависимость соответствует изменяемой внепроцессорной зависимости (mutable out-of-process dependency) в подавляющем большинстве случаев, поэтому автор оригинала использует здесь эти два понятия как синонимы. (Ознакомьтесь с предыдущим постом Владимира Хорикова, чтобы узнать больше: Unit Testing Dependencies: The Complete Guide.)
Существуют две школы модульного тестирования с собственными взглядами на то, какие типы зависимостей следует заменять на моки:
Лондонская школа (также известная как школа mockist) выступает за замену всех изменяемых зависимостей на моки.
Классическая школа (также известная как школа Детройта) выступает за замену только общих (изменяемых внепроцессорных) зависимостей.
Обе школы ошибаются в своем отношении к мокам, хотя классическая школа меньше, чем лондонская.
Моки и неизменяемые внепроцессорные зависимости.
А как насчет иммутабельных внепроцессорных (immutable out-of-process) зависимостей? Разве их не стоит мокать, по крайней мере, по мнению одной из школ? Неизменяемые внепроцессорные зависимости (например, служба API только для чтения) следует заменить тестовым двойником, но этот тестовый двойник будет стабом, а не моком.
Это опять же из-за различий между моками и стабами:
Взаимодействия с иммутабельными внепроцессорными зависимостями по определению являются входящими и, следовательно, не должны проверяться в тестах, а только заменяться шаблонными ответами (обе школы с этим согласны).
Не мокайте все изменяемые зависимости
Не стоит мокать все изменяемые зависимости. Чтобы понять, почему, нам нужно рассмотреть два типа коммуникаций в типичном приложении: внутрисистемный и межсистемный.
Внутрисистемные коммуникации являются деталями реализации, поскольку взаимодействие, через которое проходят ваши доменные классы для выполнения операции, не является частью их наблюдаемого поведения. Это взаимодействие не имеет непосредственной связи с целью клиента. Таким образом, связь с таким взаимодействием приводит к хрупким тестам.
Это свойство межсистемных коммуникаций проистекает из того, как отдельные приложения развиваются вместе. Один из основных принципов такой эволюции – обеспечение обратной совместимости. Независимо от рефакторинга, который вы выполняете внутри своего приложения, паттерн взаимодействия, который оно использует для взаимодействия с внешними приложениями, должен всегда оставаться на месте, чтобы внешние приложения могли его понять. Например, сообщения, отправляемые вашим приложением по шине, должны сохранять свою структуру и так далее.
Использование моков закрепляет контракт взаимодействия между тестируемой системой и зависимостью. Это именно то, что вам нужно при проверке связи между вашей системой и внешними приложениями. И наоборот, использование моков для проверки связи между классами внутри вашей системы связывает ваши тесты с деталями реализации, делая их хрупкими. Внутрисистемные связи соответствуют изменяемым внутрипроцессорным зависимостям:
Итак, лондонская школа ошибается, потому что она поощряет использование моков для всех изменяемых зависимостей и не проводит различия между внутрисистемными (внутрипроцессорными) и межсистемными (внепроцессорными) коммуникациями.
В результате тесты проверяют связь между классами точно так же, как они проверяют связь между вашим приложением и внешними системами. Это неизбирательное использование моков является причиной того, что следование лондонской школе часто приводит к хрупким тестам — тестам, которые связаны с деталями реализации.
Не мокайте все внепроцессорные зависимости
Классическая школа лучше справляется с этим вопросом, потому что она выступает за замену только внепроцессорных зависимостей, таких как служба SMTP, шина сообщений и т. д. Но классическая школа также не идеальна по отношению к межсистемным коммуникациям. Эта школа также поощряет чрезмерное использование моков, хотя и не так сильно, как лондонская школа.
Не все внепроцессорные зависимости следует мокать. Если внепроцессорная зависимость доступна только через ваше приложение, то связь с такой зависимостью не является частью наблюдаемого поведения вашей системы. Внепроцессорная зависимость, которая не может наблюдаться извне, по сути, действует как часть вашего приложения. Связи с такой зависимостью становятся деталями реализации: они не должны оставаться неизменными после рефакторинга и, следовательно, не должны проверяться с помощью моков.
Помните, что требование всегда сохранять схему взаимодействия между вашим приложением и внешними системами проистекает из необходимости поддерживать обратную совместимость. Вы должны поддерживать способ взаимодействия вашего приложения с внешними системами. Это потому, что вы не можете изменять эти внешние системы одновременно с вашим приложением; они могут следовать другому циклу развертывания, или вы можете просто не контролировать их.
Но когда ваше приложение действует как прокси-сервер для внешней системы, и ни один клиент не может получить к ней прямой доступ, требование обратной совместимости исчезает. Теперь вы можете развернуть свое приложение вместе с этой внешней системой, и это не повлияет на клиентов. Схема взаимодействия с такой системой становится деталью реализации.
Хорошим примером здесь является БД приложения: БД, которая используется только вашим приложением. Ни одна внешняя система не имеет доступа к этой БД. Таким образом, вы можете изменить схему взаимодействия между вашей системой и БД приложения любым удобным вам способом, если это не ломает существующую функциональность. Поскольку эта БД полностью скрыта от глаз клиентов, вы даже можете заменить ее совершенно другим механизмом хранения, и никто этого не заметит.
Использование моков для внепроцессорных зависимостей, которые вы полностью контролируете, также приводит к хрупким тестам. Вы не хотите, чтобы ваши тесты становились красными каждый раз, когда вы разбиваете таблицу в БД или изменяете тип одного из параметров в хранимой процедуре. БД и ваше приложение должны рассматриваться как одна система.
Это различие разделяет внепроцессорные зависимости на две подкатегории:
Управляемые (managed) зависимости — внепроцессорные зависимости, над которыми вы имеете полный контроль. Эти зависимости доступны только через ваше приложение; взаимодействия с ними не видны внешнему миру. Типичным примером является БД приложения. Внешние системы не имеют прямого доступа к вашей БД; они делают это через API, предоставляемый вашим приложением.
Только неуправляемые зависимости должны быть заменены моками. Используйте реальные экземпляры управляемых зависимостей в тестах.
Резюме
Существует пять вариантов тестовых двойников — dummy (манекен), стаб, spy (шпион), мок и фейк, которые можно сгруппировать всего в два типа: моки и стабы.
Spies функционально такие же, как и моки; dummy и фейки выполняют ту же роль, что и стабы.
Различия между моками и стабами:
Моки помогают имитировать и изучать исходящие взаимодействия: вызовы от SUT к его зависимостям, которые изменяют состояние этих зависимостей.
Стабы помогают имитировать входящие взаимодействия: для получения входных данных SUT обращается к своим зависимостям.
Проверка взаимодействия со стабами всегда приводит к хрупким тестам.
Тестовые двойники, заменяющие команды CQS, являются моками. Тестовые двойники, заменяющие запросы CQS, являются стабами.
Внепроцессорные зависимости можно разделить на 2 подкатегории: управляемые и неуправляемые зависимости.
Используйте реальные экземпляры управляемых зависимостей в интеграционных тестах; замените неуправляемые зависимости моками.
Моки, стабы и шпионы в Spock Framework
Spock предоставляет 3 мощных (но разных по сути) инструмента, упрощающих написание тестов: Mock, Stub и Spy.
Довольно часто коду, который нужно протестировать, требуется взаимодействовать с внешними модулями, называющимися зависимостями (в оригинальной статье используется термин collaborators, который не очень распространён в русскоязычной среде).
Модульные тесты чаще всего разрабатываются для тестирования одного изолированного класса при помощи различных вариантов моков: Mock, Stub и Spy. Так тесты будут надёжнее и будут реже ломаться по мере того, как код зависимостей эволюционирует.
Такие изолированные тесты менее подвержены проблемам при изменении внутренних деталей реализации зависимостей.
От переводчика: каждый раз, когда я использую Spock Framework для написания тестов, я чувствую, что могу ошибиться при выборе способа подмены зависимостей. В этой статье есть максимально краткая шпаргалка по выбору механизма для создания моков.
Mocks
Используйте Mock для:
Stubs
Используйте Stub для:
Spies
Бойтесь шпионов (Spy). Как сказано в документации Spock:
Подумайте дважды, прежде чем использовать этот механизм. Возможно, вам стоит изменить дизайн вашего решения и реорганизовать ваш код.
Но так уж случается, что бывают ситуации, когда мы должны работать с легаси кодом. Легаси код бывает сложно или даже невозможно протестировать при помощи моков и стабов. В этом случае есть всего один вариант решения — использовать Spy.
Лучше иметь легаси код, покрытый тестами с использованием Spy, чем не иметь тестов для легаси совсем.
Используйте Spy для:
Mocks
В этом сценарии мы хотим написать тест, который проверит:
Приведённый тест создаёт мок сервиса:
Приведённый код решает 4 важные задачи:
Stubs
Использует ли тестируемый код зависимости? Является ли целью тестирования удостовериться, что тестируемый код работает корректно при взаимодействии с зависимостями? Являются ли результаты вызовов методов зависимостей входными значениями для тестируемого кода?
Если поведение тестируемого кода изменяется в зависимости от поведения зависимостей, то вам необходимо использовать стабы (Stub).
Давайте посмотрим на следующий пример с FooController и FooService и протестируем функциональность контроллера при помощи стабов.
Создать стаб можно так:
Приведённый код решает 4 важные задачи:
Spies
Пожалуйста не читайте этот раздел.
Пропускайте и переходите к следующему.
Всё ещё читаете? Ну что ж, хорошо, давайте разбираться со Spy.
Не используйте Spy. Как сказано в документации Spock:
Подумайте дважды, прежде чем использовать этот механизм. Возможно, вам стоит изменить дизайн вашего решения и реорганизовать ваш код.
При этом бывают ситуации, когда нам приходится работать с легаси кодом. Легаси код бывает невозможно протестировать с помощью моков или стабов. В этом случае шпион остаётся единственным жизнеспособным вариантом.
Шпионы отличаются от моков или стабов, потому что они работают не как заглушки.
Когда зависимость подменяется моком или стабом, создается тестовый объект, а настоящий исходный код зависимости не выполняется.
Шпион, с другой стороны, выполнит основной исходный код зависимости, для которой был создан шпион, но шпион позволит вам изменять то, что возвращает шпион, и проверять вызовы методов, так же как моки и стабы. (Отсюда и название Spy).
Создать экземпляр-шпион довольно просто:
Приведённый код решает 4 важные задачи:
Какой из вариантов использовать: Mock, Stub или Spy?
Это вопрос, с которым сталкиваются многие разработчики. Этот FAQ может помочь, если вы не уверены, какой из подходов использовать.
Q: Является ли целью тестирования проверка контракта между тестируемым кодом и зависимостями?
A: Если вы ответили Да, используйте Mock
Q: Является ли целью тестирования удостовериться, что тестируемый код работает верно при взаимодействии с зависимостями?
A: Если вы ответили Да, используйте Stub
Q: Являются ли результаты вызовов методов зависимостей входными значениями для тестируемого кода?
A: Если вы ответили Да, используйте Stub
Q: Работаете ли вы с легаси кодом, который очень сложно протестировать, и у вас не осталось вариантов?
A: Попробуйте использовать Spy
Код примеров
Вы можете найти код всех примеров этой статьи по ссылке: