что такое mock в тестировании

Моки, стабы и шпионы в Spock Framework

Spock предоставляет 3 мощных (но разных по сути) инструмента, упрощающих написание тестов: Mock, Stub и Spy.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Довольно часто коду, который нужно протестировать, требуется взаимодействовать с внешними модулями, называющимися зависимостями (в оригинальной статье используется термин collaborators, который не очень распространён в русскоязычной среде).

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

Такие изолированные тесты менее подвержены проблемам при изменении внутренних деталей реализации зависимостей.

От переводчика: каждый раз, когда я использую Spock Framework для написания тестов, я чувствую, что могу ошибиться при выборе способа подмены зависимостей. В этой статье есть максимально краткая шпаргалка по выбору механизма для создания моков.

Mocks

Используйте Mock для:

Stubs

Используйте Stub для:

Spies

Бойтесь шпионов (Spy). Как сказано в документации Spock:

Подумайте дважды, прежде чем использовать этот механизм. Возможно, вам стоит изменить дизайн вашего решения и реорганизовать ваш код.

Но так уж случается, что бывают ситуации, когда мы должны работать с легаси кодом. Легаси код бывает сложно или даже невозможно протестировать при помощи моков и стабов. В этом случае есть всего один вариант решения — использовать Spy.

Лучше иметь легаси код, покрытый тестами с использованием Spy, чем не иметь тестов для легаси совсем.

Используйте Spy для:

Mocks

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

В этом сценарии мы хотим написать тест, который проверит:

Приведённый тест создаёт мок сервиса:

Приведённый код решает 4 важные задачи:

Stubs

Использует ли тестируемый код зависимости? Является ли целью тестирования удостовериться, что тестируемый код работает корректно при взаимодействии с зависимостями? Являются ли результаты вызовов методов зависимостей входными значениями для тестируемого кода?

Если поведение тестируемого кода изменяется в зависимости от поведения зависимостей, то вам необходимо использовать стабы (Stub).

Давайте посмотрим на следующий пример с FooController и FooService и протестируем функциональность контроллера при помощи стабов.

Создать стаб можно так:

Приведённый код решает 4 важные задачи:

Spies

Пожалуйста не читайте этот раздел.

Пропускайте и переходите к следующему.

Всё ещё читаете? Ну что ж, хорошо, давайте разбираться со Spy.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Не используйте Spy. Как сказано в документации Spock:

Подумайте дважды, прежде чем использовать этот механизм. Возможно, вам стоит изменить дизайн вашего решения и реорганизовать ваш код.

При этом бывают ситуации, когда нам приходится работать с легаси кодом. Легаси код бывает невозможно протестировать с помощью моков или стабов. В этом случае шпион остаётся единственным жизнеспособным вариантом.

Шпионы отличаются от моков или стабов, потому что они работают не как заглушки.

Когда зависимость подменяется моком или стабом, создается тестовый объект, а настоящий исходный код зависимости не выполняется.

Шпион, с другой стороны, выполнит основной исходный код зависимости, для которой был создан шпион, но шпион позволит вам изменять то, что возвращает шпион, и проверять вызовы методов, так же как моки и стабы. (Отсюда и название Spy).

Создать экземпляр-шпион довольно просто:

Приведённый код решает 4 важные задачи:

Какой из вариантов использовать: Mock, Stub или Spy?

Это вопрос, с которым сталкиваются многие разработчики. Этот FAQ может помочь, если вы не уверены, какой из подходов использовать.

Q: Является ли целью тестирования проверка контракта между тестируемым кодом и зависимостями?

A: Если вы ответили Да, используйте Mock

Q: Является ли целью тестирования удостовериться, что тестируемый код работает верно при взаимодействии с зависимостями?

A: Если вы ответили Да, используйте Stub

Q: Являются ли результаты вызовов методов зависимостей входными значениями для тестируемого кода?

A: Если вы ответили Да, используйте Stub

Q: Работаете ли вы с легаси кодом, который очень сложно протестировать, и у вас не осталось вариантов?

A: Попробуйте использовать Spy

Код примеров

Вы можете найти код всех примеров этой статьи по ссылке:

Источник

Mockito и как его готовить

О статье

Перед вами очередное руководство по Mockito. В нём я, с одной стороны, попытался описать функционал этой библиотеки так, чтобы незнакомый с нею читатель сразу получил возможность полноценно ею пользоваться, а не только общее представление о ней. С другой — я хотел сделать его достаточно компактным и структурированным, чтобы можно было быстро прочесть его целиком и быстро найти в нём что-то единожды прочитанное, но подзабытое. В общем, эта такая статья, какая бы пригодилась мне самому, когда я только столкнулся с этой библиотекой и не очень понимал, как она работает.

Полагаю, она и сейчас может пригодиться мне же — иногда я забываю что-то из этого, а вспоминать материал удобнее всего не по официальной документации или чужим статьям, а по собственному, скажем так, конспекту. Вместе с тем я старался построить текст так, чтобы он был удобен прежде всего для знакомства с Mockito с нуля, и кое-где подробно разбираю вроде бы очевидные вещи — не все из которых были для меня очевидными с самого начала.

Содержание:

Mockito: что это такое и зачем нужно

Говоря коротко, Mockito — фреймворк для работы с заглушками.

Как известно, при тестировании кода (прежде всего юнит-тестировании, но не только) тестируемому элементу часто требуется предоставить экземпляры классов, которыми он должен пользоваться при работе. При этом часто они не должны быть полнофункциональными — наоборот, от них требуется вести себя жёстко заданным образом, так, чтобы их поведение было простым и полностью предсказуемым. Они и называются заглушками (stub). Чтобы их получить, можно создавать альтернативные тестовые реализации интерфейсов, наследовать нужные классы с переопределением функционала и так далее, но всё это достаточно неудобно, избыточно и чревато ошибками. Более удобное во всех смыслах решение — специализированные фреймворки для создания заглушек. Одним из таковых (и, пожалуй, самым известным для Java) и является Mockito.

Mockito позволяет создать одной строчкой кода так называемый mock (что-то вроде основы для нужной заглушки) любого класса. Для такого mock сразу после создания характерно некое поведение по умолчанию (все методы возвращают заранее известные значения — обычно это null либо 0 ). Можно переопределить это поведение желаемым образом, проконтролировать с нужной степенью детальности обращения к ним так далее. В результате mock и становится заглушкой с требуемыми свойствами. Ниже я подробно разберу, как это сделать.

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

Окружение, версии и подопытное животное

При написании этой статьи я использовал:

Для своих бесчеловечных экспериментов я написал вот такой интерфейс сервиса, предоставляющего доступ к неким данным.

А это (пусть уж будет для порядка) код класса запроса, передаваемого в последний из методов интерфейса.

Единицы данных идентифицируются по ID и имеют ещё некоторые характеристики, но непосредственно в том виде, в котором возвращаются сервисом, они представляют собой строки, а не какие-то более сложные объекты. Ничего важного я так не упускаю, а примеры получаются проще и нагляднее.

Сразу же отмечу: в примерах ниже я для наглядности непосредственно вызываю переопределённые методы моих mock-объектов, но при реальном тестировании идея вовсе не в этом! В настоящем тесте я бы последовательно выполнил следующее:

mock и spy

Особо нужно упомянуть ситуацию, когда я пытаюсь создать mock для final класса или mock-экземпляр enum либо переопределить поведение final метода. В таком случае при поведении Mockito по умолчанию код выше откажется работать, сославшись именно на это обстоятельство. Однако это можно изменить — достаточно создать в проекте (при стандартном устройстве проектного дерева каталогов) файл test/resources/mockito-extensions/org.mockito.plugins.MockMaker и вписать в него строчку:

После этого можно имитировать обычным способом final классы и enum’ы, а также переопределять final методы.

Так или иначе, в большинстве случаев мне понадобится не поведение по умолчанию, и в следующем разделе я подробно разберу, как задавать вместо него то, которое требуется.

Однако что, если я хочу использовать в качестве заглушки объект реального класса с имеющимся функционалом, переопределив работу только части его методов? Если речь о юнит-тестировании, такая потребность обычно (но не всегда) свидетельствует о том, что в проекте не всё в порядке с дизайном, и в принципе так действовать не рекомендуется. Однако случаются ситуации, когда этого по какой-то причине не избежать. На этот случай в Mockito есть так называемые spy, «шпионы». В отличие от mock’ов, их можно создавать на основе как класса, так и готового объекта:

При создании spy на основе класса, если его тип — интерфейс, будет создан обычный mock-объект, а если тип — класс, то Mockito попытается создать экземпляр при помощи конструктора по умолчанию (без параметров). И только если такого конструктора нет, произойдёт ошибка и тест не сработает.

Поведение spy-объектов по умолчанию идентично поведению обычного экземпляра класса, однако они дают мне те же возможности, что и mock-объекты: позволяют переопределять их поведение и наблюдать за их использованием (см. следующие разделы). Важный момент: spy — не обёртка вокруг того экземпляра, на основе которого он создан! Поэтому вызов метода spy на состояние изначального экземпляра не повлияет.

Управление поведением

Итак, о том, как заставить mock или spy делать то, что мне нужно. Далее я буду везде писать просто «mock» — это будет значить «mock или spy», кроме случаев, где прямо сказано иное.

В целом управление поведением mock-объекта сводится к одной очевидной концепции: когда на mock так-то воздействовали (то есть вызван такой-то метод с такими-то аргументами), он должен отреагировать так-то и так-то. У этой концепции существуют две реализации в рамках класса Mockito — основная, рекомендуемая разработчиками к использованию везде, где это возможно, и альтернативная, применяемая там, где основная не годится.

Здесь привычная «объектно-ориентированная» интуиция может дать некоторый сбой, так что на этом стоит остановиться чуть подробнее. С точки зрения синтаксиса Java значением, передаваемым методу when в качестве параметра, является, разумеется, значение, возвращаемое переопределяемым методом. Для mock это пустое значение, для spy — значение, возвращаемое методом реального объекта. Но благодаря действующей «под капотом» Mockito магии метод when сработает штатным образом (а не упадёт при запуске с ошибкой) лишь в том случае, если внутри скобок после when находится именно вызов метода mock-объекта.

Подобная идеология часто действует при задании поведения mock в Mockito: вызывая метод (mock-объекта или класса Mockito ), я стремлюсь не получить возвращаемое им значение, а каким-то образом повлиять на возможный вызов того метода mock-объекта, с которым работаю: указать его границы, задать результат, установить наблюдение за его вызовами и так далее. Звучит несколько туманно, признаю, и при первом столкновении выглядит странно, но, разобравшись, очень скоро начинаешь ощущать этот подход как совершенно естественный в контексте работы с заглушками.

Далее я рассмотрю чуть подробнее способы задания условий и результатов вызовов. Я буду рассматривать только связывание через Mockito.when — альтернативный способ практически полностью аналогичен в обращении.

Задание условий вызова

Пример выше касается метода без параметров, и связанное с ним условие вызова возможно одно — сам факт вызова. Как только появляются параметры, ситуация становится сложнее. Как минимум, для вызова метода, поведение которого я задаю, мне нужно что-то ему передать. Но важнее другое: может оказаться, что задаваемую реакцию я хочу получать не всегда, а только при вызове с параметрами, отвечающими определённым требованиям. DataService имеет вот такой метод:

Если мне нужно задать реакцию на любой вызов этого метода независимо от аргументов, я должен воспользоваться методом Mockito.any :

Если же мне требуется, чтобы mock реагировал только на определённое значение аргумента, можно использовать непосредственно это значение или методы Mockito.eq (когда речь об эквивалентности) либо Mockito.same (когда требуется сравнение ссылок):

А если я хочу, чтобы аргумент отвечал каким-то требованиям, для этого есть ряд удобных специализированных статических методов того же класса Mockito (например, строки можно проверить на содержание в начале или в конце определённой последовательности символов, соответствие паттерну и др.). Также имеется общий метод Mockito.argThat (и его аналоги для примитивных типов), принимающий реализацию функционального интерфейса ArgumentMatcher:

Классы ArgumentMatchers и AdditionalMatchers позволяют работать с некоторыми полезными готовыми реализациями этого интерфейса. Например, AdditionalMatchers.or и AdditionalMatchers.and позволяют комбинировать другие матчеры (обратите внимание: статические методы этих классов не возвращают экземпляры матчеров, а только обращаются к ним!)

Для одного и того же метода можно задать поведение несколько раз с разными требованиями к аргументам, и все определённые таким образом модели поведения будут действовать одновременно. Разумеется, в каких-то случаях они могут пересекаться — скажем, я потребую вернуть один результат при получении значения int параметра меньше 5 и другой — при получении чётного значения. В такой ситуации приоритет имеет то поведение, которое задано позже. Поэтому при задании сложных схем поведения следует начинать с самых слабых требований (в пределе — any() ) и уже затем переходить к более специфическим.

При работе с методами с более чем одним аргументом заданные требования комбинируются в соответствии с логическим И, то есть для получения заданного результата КАЖДЫЙ из аргументов должен отвечать поставленному требованию. Я не нашёл способа скомбинировать требования произвольным образом, хотя, возможно, он существует.

Задание результатов вызова

После того, как метод mock-объекта вызван, объект должен отреагировать на вызов. Основные возможные последствия — возвращение результата и выбрасывание исключения, и именно на эти варианты в первую очередь рассчитан инструментарий Mockito.

В простейшем случае, уже показанном выше, реакция на вызов — возвращение значения. Приведу его код ещё раз:

Обратите внимание: вернуть можно только объект, отдельных методов для примитивов не предусмотрено. Поэтому, если метод возвращает примитивное значение, в такой ситуации будет происходит un/boxing. В большинстве случаев это никак не мешает, но если компилятор считает иначе, придётся как-то с ним договариваться… или смириться с его предупреждениями.

Бросить исключения ничуть не сложнее:

Есть и другой способ: можно создать объект исключения и бросить непосредственно его, а можно предоставить Mockito только класс исключения, чтобы оно было создано автоматически:

В обоих случаях синтаксис позволяет использовать и checked исключения, однако Mockito не позволит запустить такой тест, если тип исключения не соответствует методу, который я хочу заставить бросить это исключение.

Идеологически это что-то вроде написания модели реального метода: получение параметров, обработка, возвращение результата. В принципе ничто не мешает мне реализовать здесь и какую-то другую логику, дополнительно влияющую на что-нибудь ещё, но если такая необходимость возникает регулярно, это может указывать на то, что я заставляю mock-объекты выполнять несвойственные им задачи.

Такой вариант реакции также можно получить через thenAnswer : объект InvocationOnMock имеет метод callRealMethod() — это может пригодиться, если нужно «обернуть» вызов реального кода какой-то дополнительной логикой.

Слежение за вызовами методов

Простейший вариант, когда я проверяю факт однократного вызова метода на протяжении выполнения теста, выглядит так:

То же самое можно выразить при помощи более гибкого перегруженного метода:

В этом случае проверка, не обнаружившая у mock нужного числа вызовов, не приводит сразу к падению теста, а сперва ждёт в течение заданного в миллисекундах периода времени в расчёте на то, что эти вызовы всё же будут совершены. Это возможность полезна при работе с многопоточным кодом. Между собой after и timeout различаются тем, что в первом случае проверка успешно проходит только после того, как заданный период завершится, а во втором — сразу после того, как требуемое условие окажется выполненным. Таким образом, при использовании timeout вызовов соответствующего метода может оказаться и больше требуемого — на успешность прохождения теста это не повлияет. Поэтому у VerificationWithTimeout нет методов never и atMost : с учётом принципа его работы в них нет смысла.

В конце я вызываю метод verifyNoMoreInteractions (он же verifyZeroInteractions ) — он проверяет отсутствие каких-либо неверифицированных (то есть не подпадающих ни под один из выполненных до этого вызовов verify ) обращений к моему mock-объекту — к любым его методам. Обратите внимание: метод принимает varargs, но это совсем не означает, как можно было бы подумать, что речь о проверке взаимодействия переданных ему объектов между собой!

Код выше проверяет факт определённого количества вызовов, но не то, в каком порядке они были совершены, а это тоже может понадобиться. Чтобы контролировать порядок, нужно получить объект InOrder :

Этот метод тоже принимает varargs; порядок добавления не важен — несколько переданных ему mock-объектов означают всего лишь, что полученный объект InOrder можно будет использовать для контроля за порядком вызова методов методов всех этих объектов относительно друг друга. Сам метод имеет методы verify с теми же сигнатурами, что и Mockito.verify :

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

Mock-объекты как значения полей и аннотации Mockito

Если в классе теста есть поля, которым я хочу присвоить mock-объекты в качестве значений, это не обязательно делать вручную — достаточно снабдить его аннотацией @Mock и до каких-либо обращений к нему выполнить вот такой вызов:

(несмотря на название, этот метод предназначен не только для mock’ов, а задействует также и все нижеперечисленные аннотации)

Для spy предусмотрена аннотация @Spy — она в целом аналогична @Mock … но для spy может использоваться объект, на основе которого он будет создан, помните? Такой объект можно сразу указать в качестве значения аннотируемого поля, но можно и не указывать — тогда spy будет создан на основе класса.

Есть аннотация @Captor для создания экземпляров ArgumentCaptor — о ней отдельно, пожалуй, больше ничего не скажешь.

Откат поведения к дефолтному и сессии Mockito

Если в моём тестовом классе всего один тестирующий метод, всё отлично: я создал mock (spy, argument captor. ), задал ему поведение, использовал его в тесте, всё. Но если их больше, а mock’и — это поля тестового класса, всё может оказаться сложнее. JUnit создаёт отдельный экземпляр тестового класса при вызове каждого из тестирующих методов, и у него здесь проблем нет, а вот у TestNG другой подход — экземпляр создаётся лишь один на все вызовы. Соответственно, поведение, определённое для mock’ов в одном из методов, будет воспроизводиться и в других, выполняемых после него, количество вызовов методов будет суммарным для них и т. д. А это, скорее всего, нежелательно — тем более, что порядок выполнения тестовых методов в общем случае не гарантирован.

Чтобы этого избежать, нужно до вызова каждого тестирующего метода тем или иным способом привести все mock-объекты в состояние по умолчанию. У TestNG есть для этого аннотации @BeforeMethod (и @AfterMethod для постобработки). После этого часто бывает удобно заново задать желаемое поведение для mock’ов в той степени, в которой оно общее для всех тестовых методов, чтобы на долю самих методов осталось лишь задание специфических деталей (это актуально и для JUnit — у него есть аналогичная аннотация @Before ).

Кроме того, сессии позволяют контролировать ещё некоторые аспекты работы — в частности, отслеживают «лишние» (заданные, но не задействованные в тесте) варианты поведения, если это нужно.

Что ещё?

Выше я рассмотрел основные возможности Mockito: создание mock и spy-объектов, задание из поведения и наблюдение за их использованием. Это не весь функционал этой библиотеки, но основная и чаще всего используемая часть. Не упомянутыми, в частности, остались:

Источник

Использование mock при интеграции с внешним API

Тестирование интеграции с API

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Задача интеграции с API встречается очень часто, по популярности она уступает только обмену между базами и выгрузке из Excel.

Есть много разных вариантов API:

Есть мастодонты – Google, Яндекс – у них много разных сервисов, с которыми можно интегрироваться. Например, получение переводов, генерация и чтение текстов, прогнозы погоды.

Часто приходится интегрироваться с «Почтой России» или OZON.RU – если ваша компания занимается перевозками или отправкой товаров.

С Telegram – если надо отправлять или получать данные и писать ботов.

И, конечно, наши государственные сервисы: например, ФНС, если вам нужно получать данные о контрагентах.

У всех этих вариантов есть нечто общее:

Клиент-серверное взаимодействие, когда вы от себя отправляете данные, а все обрабатывается на стороне сервиса.

Сервер не будет хранить ваше состояние – ему без разницы, какой вы запрос отправите, состояние запроса вы должны соблюдать самостоятельно.

Используется немного кэширования.

Формат ответа – обычно HTML, XML, JSON либо бинарные файлы (если вы что-то получаете в виде картинок или pdf-документов).

Проблемы тестирования

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Какие проблемы возникают в тестировании с API?

Основное – стабильность тестов, потому что когда у вас внешнее API – вы целиком и полностью зависите от того, что происходит на сервере. Сервис может уйти на обслуживание, не выдержать нагрузки и, соответственно, ваши тесты тоже перестают проходить.

Возможность покрытия сценариев. Тяжело реализовать все сценарии, которые вы можете придумать, когда у вас внешнее API.

Большая проблема – скорость ответов. Зависит от того, какая нагрузка на сервере, которым вы пользуетесь, сколько до вас идет ответ.

Следующая проблема – риск работы с реальными данными. Если вы тестируете наживую, то можете пересечься с вашими реальными данными, которыми оперирует учетная система. Ничем хорошим это не закончится.

Еще есть проблема: можно увлечься, и вместо того, чтобы тестировать, как ваше приложение взаимодействует с API, начать тестировать API. Вы думаете: «А что будет, если я отправлю такой запрос, а если такой, а если здесь пробел поставлю?» И получается, что вместо того, чтобы придумать сценарий, как вы будете взаимодействовать с сервером, вы смотрите, как ответит сервер.

Методы решения проблем

Какие есть методы решения проблем?

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Самый простой вариант – вам предоставят тестовый стенд. Вам выделяют отдельную область, в которой вы будете тестировать свои алгоритмы.

В чем тут может быть проблема? Если стенд один, а разработчиков несколько – ваши тесты могут мешать друг другу.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Следующий вариант – написать свой сервер.

Все программисты, если что-то не работает на стороне, начинают писать свое. Делаем небольшой сервер – берем Python или OneScript.Web – и пишем небольшие заглушки. Мы знаем, какие запросы отправляем, готовим под них ответы.

В чем здесь проблема? Его надо писать. Это будет ваше отдельное приложение, которое надо поддерживать.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Следующий вариант – использовать специальные mock-сервера (заглушки).

Для них все равно нужно будет готовить данные.

При этом вам нужно не забывать за этими данными следить – вы поставили заглушку, у вас тестирование прекрасно проходит, но если на «боевой» уже поменялся API, и у вас тестируется не реальная ситуация.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Последний вариант – решить все архитектурно, написать приложение так, чтобы оно само в себе содержало тестовые данные и могло их в любой момент использовать. Это самый сложный вариант, мы не будем на нем останавливаться

Мы разберем мокирование.

Мокирование

Мокирование от английского mock – «заглушка». Выглядит это следующим образом.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

В первую очередь, тест готовит себе данные – обращается к специально запущенному приложению, передает ему информацию о том, что собирается отправить в него такие-то данные и ожидает получить на них такие-то ответы.

Далее тест обращается к вашей системе, чтобы она отправила запросы не на реальный сервер, а на заглушку.

Заглушка присылает ответы, а тест верифицирует: правильные ли данные получила система.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Вариантов таких мок-серверов много, перечислю самые популярные.

Один из первых, который мне попадался – это Postman, в нем есть возможность мокирования. Postman – одно из мастхэв-решений, если вы пишете интеграцию. Работать с запросами в нем очень удобно. И также в нем есть собственная возможность мокирования. Достаточно слабенькая, но есть.

SoapUI – достаточно мощное решение, там есть запись ваших запросов, их воспроизведение, упаковка в сценарии. Достаточно популярное решение для мокирования.

Есть сервис МоckServer. С ним я еще не разобрался, он достаточно сложный.

И есть JSON-Server – легкое решение, в нем есть база данных, основанная на JSON. Он умеет сохранять данные и преобразовывать.

Но я вам хочу рассказать про работу с WireMock.

WireMock – запуск, возможности, управление

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Что такое WireMock?

WireMock – приложение на Java, что уже хорошо. Значит, вы сможете его запускать и на Windows, и на Linux.

Он умеет работать в режиме JUnit, когда вы пишете тесты на Java, и Standalone – как отдельно запускаемое приложение. С 1С я его чаще использую как Standalone.

Он очень хорошо управляется через HTTP-запросы. Когда вы его как Standalone подняли, он управляется, отправляя запросы на нужный адрес.

Все настройки он хранит в JSON-формате – это очень удобно, потому что JSON хорошочитаемый, его удобно смотреть при code-review.

У него есть функция записи и воспроизведения – чуть позже расскажу, что это такое.

Также он умеет назначать приоритеты шаблонам, умеет работать с состояниями и симулировать ошибки.

Чтобы было понятнее – давайте разберем конкретную задачу.

Практика

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Наша задача – сделать погодный виджет для Бухгалтерии 3.0. Я достаточно часто встречал такие обработки на Инфостарте – видимо, многие клиенты хотят знать погоду, не выходя из 1С.

Мы выведем на главной странице Бухгалтерии 3.0 текущий прогноз погоды и постараемся максимально закрыть это тестами.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Реализация выглядит вот так – это расширение, которое я нашел на Инфостарте в публикации //infostart.ru/public/801039/. Оно выводит погоду – сейчас +18 градусов.

Давайте накроем его код тестом.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Тест делаем как обычно, кнопконажималкой – запускаем 1С, получаем нужный элемент и проверяем его данные.

Насколько это будет стабильный тест? С утра у нас было +7, днем стало +15, два часа назад пошел дождь, но теперь светит солнце. Соответственно, тест будет совершенно нестабилен – каждый раз, когда меняется погода, тест будет падать.

Как раз его мы и попробуем замокировать.

Подготовка

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Первым делом, нужно провести подготовку.

Настроить запуск 1С в режиме тестирования.

Научиться перенаправлять запросы – чтобы запросы отправлялись не к внешнему сервису, а к нашему.

Подготовить набор ответов.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Начнем с запуска в режиме тестирования. Я обычно реализую такой метод через параметр сеанса, который зависит от параметра запуска.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Теперь насчет подготовки мокированных ответов.

У WireMock есть специальноеприложение – WireMockUI, которое позволяет настроить следующую вещь: мы в нем указываем, какой сервис API мы хотим мокировать, на какой сервер мы его перенаправляем.

Дальше мы его запускаем в режиме записи. Можем сделать все, что нам нужно в 1С или приложении. WireMock перехватывает эти запросы и готовит необходимые файлики ответов и шаблоны для запросов. Мы это забираем и запускаем с уже заготовленными шаблонами.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Вот так это выглядит. Мы включили запись, что-то потыкали, получили нужные ответы, забираем эти результаты себе. Складываем это в какую-то папку с проектом или еще куда. И после этого запускаем WireMock с указанием того, что все наши шаблоны лежат в нужной папке.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Шаблон выглядит вот так: здесь URL запроса и ответ.

Содержание большого JSON-ответа можно упаковать в отдельный файлик – его адрес будет указан в свойства bodyFileName.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Вот так выглядит этот JSON-файл ответа.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Теперь мы можем переписать наш тест следующим образом – указываем, где у нас живет WireMock со всеми настройками, запускаем его. По итогу тест получается стабильным. Сколько бы раз мы его ни запускали, у нас получится одна и та же температура на главной странице.

Макеты

Давайте подробнее разберемся, что такое макет.

Макет – это шаблон, по которому будет определяться, откуда брать ответ. Он должен описать вашу строку запуска, включая переданные в нее параметры и заголовки.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Самое первое, что есть в шаблонах – какой HTTP-метод мы используем.

Например, можно указать, что этот шаблон будет использоваться для обработки всех запросов на такой-то адрес, которые используют метод GET.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Дальше мы разбираем, куда уходит наш запрос.

Первый вариант – использовать свойство url, которое описывает полное соответствие, включая все параметры. Мы прямо говорим, что если мы делаем GET-запрос по такому-то URL – возвращай ответ. Это не очень удобно, потому что в коде могут поменяться местами параметры, некоторые параметры будут являться обязательными или необязательными.

Поэтому следующий вариант – в свойстве urlPattern написать регулярное выражение, которое будет разбирать вашу строку.

И совсем простой вариант – с помощью свойства urlPath указывать только начальный путь. Чаще всего я использую именно этот вариант, когда указываю, что запрос будет по такому-то URL, а потом отдельно обрабатываю параметры запроса через свойство queryParameters.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Есть несколько вариантов обработки параметров:

equalTo – это точное соответствие, когда вы говорите, чему должен быть равен этот параметр;

matches – регулярное выражение, которому этот параметр должен соответствовать.

Если параметр необязательный, его все равно нужно указать. Ведь если запрос придет с этим параметром, то шаблон в этом случае уже не сработает.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

С помощью свойства bodyPatterns есть возможность обрабатывать тело запроса. В зависимости от того, какой запрос вы обрабатываете, можно использовать:

XPath-язык – свойство matchesXPath;

JSONPath – свойство matchesJsonPath;

или описывать полное совпадение тела запроса через свойства equalToXml и equalToJson.

Это все, что касается шаблона запроса.

Дальше этот шаблон переадресовывает к шаблонам ответа. То есть, для каждого шаблона на запрос мы готовим ответ.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Вы можете готовить динамические ответы:

указывать дату в определенном формате;

разбирать входящие параметры, если это требуется;

там есть конкатенация строк и т.д.

Но динамические шаблоны ответа – это не очень хорошая практика, потому что, по-хорошему, вы должны готовить шаблоны под каждый тест.

Stateful

Разберемся, что делать, если у нас есть зависимость от состояния. Сервер не хранит состояния обмена по API, но бэкэнд может какие-то состояния хранить.

Например, если у вас сервис учитывает контроль остатков. Вы делаете запрос остатков, списываете товар, делаете еще один запрос – у вас товар должен закончиться. Получается, у вас есть несколько состояний.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

WireMock позволяет это реализовать:

у него есть понятие сценария;

каждый сценарий должен находиться в определенной стадии;

эти стадии можно переключать как прямым запросом по HTTP, так и самим сценарием.

Например, здесь на слайде указан сценарий «To do list», и текущее состояние у него стартовое (“requiredScenarioState”: “Started”).

Давайте реализуем такой контроль состояний у нас – чтобы для разных состояний за окном погода на виджете менялась.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Нам потребуется два шаблона:

один стартовый (“requiredScenarioState”: “Started”),

другой я назвал bad (“requiredScenarioState”: “bad”) – сейчас объясню, почему.

Логика такая – когда наш сервер стартует, он находится в стартовом состоянии и подает нужный ответ.

А когда мы переключим его в новое состояние, данные на виджете должны измениться.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Как это будет выглядеть в тесте? Мы указываем, что работаем со сценарием Weather.

Прямо в коде явно переключаем его состояние – тогда через 30 секунд у нас меняется погода на виджете.

Аварийные ситуации – низкая скорость, обрыв, мусор

Плохо, когда пользователю показывают сообщения об ошибке. Они чаще всего пугаются, их закрывают и не сознаются, что у них были ошибки вообще.

Лучше постараться обработать эти аварийные ситуации – повторно отправить запрос или подсказать пользователю.

Но когда вы работаете с реальным API, с тем же тестовым стендом, протестировать низкую скорость или обрыв соединения – большая проблема. Проблема даже вручную это протестировать: наш тестировщик это проделывал, выключая вай-фай во время теста.

Как это можно реализовать с помощью мокирования?

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Первое – задержка ответа. Прямо в шаблоне можно указать, что нам потребуется какая-то задержка.

Она бывает фиксированная, когда вы говорите, что каждый запрос должен быть задержан на 1000 миллисекунд, на 3 минуты.

Есть вариант показать, что это уже случайная величина на каждый запрос.

И есть интересный вариант – цепочка ответов, когда ваш запрос идет с задержкой в 1000 миллисекунд пятью пачками (каждые 200 миллисекунд отправляет пачку ответов) и в конце концов собирается. На слайде показано, как это может быть реализовано.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Еще интересный вариант, что делать, если вам пришел вообще невалидный ответ сервера:

вам может прийти пустой запрос;

может прийти мусор в ответе – когда вначале был json, а потом абракадабра;

и соединение может быть закрыто во время получения данных.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

На слайде показано, как выглядит запрос Postman, когда приходит совсем пустой ответ – он не может ничего прочитать.

Если вы перехватите такую ошибку, 1С это более-менее переваривает.

что такое mock в тестировании. Смотреть фото что такое mock в тестировании. Смотреть картинку что такое mock в тестировании. Картинка про что такое mock в тестировании. Фото что такое mock в тестировании

Интересный вариант реализации – как протестировать обрыв связи. Реализуется как раз цепочками состояний.

есть стартовое состояние – Started;

дальше мы получаем валидное состояние с погодой – Good;

переключаем наш сценарий запросом CONNECTION_RESET_BY_PEER на состояние Conn_reset («Оборвать соединение»);

и снова переключаем на хорошее – Good.

Таким образом проверяем, что в случае обрыва соединения будет попытка его восстановить, и пользователю будет выведено сообщение.

Резюме

Как итог всего этого – вы получаете:

стабильную проверку позитивных и негативных сценариев, не угрожая продуктовому серверу;

вполне параллельно тестируете локально – тестировщики тестируют у себя, одновременно гоняются тесты на сервере, все замечательно;

также можете проверять неадекватные ситуации – сбои, менять ответы, если что-то пошло не так, мусор в ответы наваливать.

Все это позволит вам повышать качество ваших продуктов.

Полезные ссылки

Накину чуть-чуть полезных ссылок, что можно почитать подробнее:

Статья на Инфостарте //infostart.ru/public/1014870/ про использование SoapUI в качестве заглушек.

Мой демо-стенд: https://github.com/KrapivinAndrey/infostart2020-DevOps1c-Mockdemo – можете зайти и посмотреть все сценарии, которые я вам показал. Плюс еще парочка реализованных. Возьмите и попробуйте их локально позапускать, посмотреть, как это все устроено.

Вопросы

Где взять шаги для фич, чтобы работать с WireMock?

Шаги можно взять там же, у меня в репозитории

Пробовали ли вы использовать OneScript.Web в качестве мок-сервера?

Как я уже сказал, когда пишешь сам, проблема в том, что нужно писать. Первый сценарий мы реализовали на Python, потом переписали на OneScript.Web. Но когда начинается усложнение системы, ты понимаешь, что у тебя есть твой код, теперь тебе еще нужно поддерживать свой мок-сервер. Зачем, если уже люди постарались и написали готовое решение.

Как изменяется состояние сервера? Это какая-то опция в командной строке его запуска?

Во-первых, нужно создать шаблон ответа для этой стадии при этом имени сценария. Само состояние меняется сценарием – там есть шаг, который напрямую направляет запрос к WireMock и меняет стадию. И при выполнении запроса есть опция, что этот запрос переключает на следующую стадию.

Непонятно, в каком формате смотреть результаты тестов.

Результаты тестов смотреть в привычном формате – Allure, Junit. Как обычно, мы проверяем результаты нашего тестирования в фреймворке тестирования – например, Vanessa ADD. Мы не тестируем API, мы тестируем поведение нашей системы – мы отправляем запрос, ожидаем, что поле будет иметь такое значение. Если это не так, очевидно, что тест провален.

Данная статья написана по итогам доклада (видео), прочитанного на онлайн-митапе «DevOps в 1С: Тестирование и контроль качества решений на 1С». Больше статей можно прочитать здесь.

Источник

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

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