Что нужно знать про Postman: максимально коротко о Mock Servers, Flow и Visualize
На просторах интернета часто встречается информация о платформе Postman. Большинство статей включают информацию о переменных, различных скриптах и автоматизации при тестировании. Но на самом деле Postman – это не только инструмент для тестирования, а платформа, которая помогает с помощью обширного набора инструментов ускорить жизненный цикл разработки API — проектирование, тестирование, документирование, имитацию и совместное использование проектов.
В этой статье я решил сделать краткий обзор функциональности Visualize, Mock Servers и Flow.
Создание Mock Service
Начнем с создания Mock-сервера, который позволяет имитировать взаимодействие сервисов. Функционал Postman позволяет легко редактировать и масштабировать мок-сервера, а одной из особенностей настройки сервера является возможность генерировать случайные значения в теле ответа.
Для создания Mock-сервера на левой панели выбираем Mock Servers → Create Mock Servers (возможно создание нажатием на +).
- Настроим свой сервер в таблице (в дальнейшем все параметры можно изменить):
Request Method
Request URL
Response Code
Response Body
Удобнее задать на следующих этапах
- Заполним поле Mock server name и укажем окружение переменных (Environment). Доступны опции:
- Сохранить сервер в качестве переменной (будет создано новое окружение переменных);
- Сделать сервер приватным. В данном случае, в запросах необходимо использовать x-api-key сгенерированный в Postman;
- Имитировать фиксированную сетевую задержку.
Результаты создания сервера:
- Система генерирует уникальный url созданного сервера;
- Система создает коллекцию с указанным названием сервера, в нашем случае это будет HabrMock;
- Система формирует запросы вида >+Request URL и размещает их в коллекции HabrMock.
Настроим сервер для POST orders/habr.
- Исправим название сервиса Default, на код ответа, который будет обрабатывать сервер (201).
- Зададим параметры ответа сервера:
Существуют 3 способа сформировать данные в ответе сервера:
Способ
Пример
Ответ с динамической переменной
Динамическая переменная предоставляет случайные данные. Чтобы использовать динамические переменные в сценариях предварительного запроса или тестирования (вкладки pre-request-Script и Tests), вам необходимо использовать функцию pm.variables.replaceIn() , например, pm.variables.replaceIn(‘>’) . Со списком динамических переменных можно ознакомится по ссылке.
На примере это будет выглядеть так:
Запрос POST >/orders/habr :
Mock Servers body:
На данном этапе мы сформировали Mock Server для запроса POST, который имитирует создание order, в теле ответа мы возвращаем всю необходимую информацию. Данный сервер можно использовать для тестирования или беспрерывной разработки.
Cценарий Flow
Flow (потоки) – новый функционал Postman. Недавно закончилось его бета-тестирование и добавлена стабильная и рабочая версия. Основной идеей Flow является разработка тестовых сценариев без использования кода во вкладке «tests» и «Pre-reques script».
Например, для объявления переменной, которую мы хотим извлечь из тела ответа, во вкладке «tests» необходимо прописать код:
var jsonData = JSON.parse(responseBody); pm.environment.set("number", jsonData.id);
При использовании Flow можно обойтись без кода:
- Добавим первый блок «Send request». Выберем запрос и окружение переменных:
- Добавим блок «Create Durables» и соединяем Response – Data:
- «Create Durables» предназначен для определения переменных. В конфигурации блока необходимо заполнить таблицу:
Key
Value
(Указываем ключ, данное значение будет использовано в следующем запросе)
(Какое значение будет найдено и использовано, в данном примере number. После первого вызова, выстраивается вложенность данных, которую удобно выбирать из списка)
- Добавим блок «Send request», в котором будем использовать значение numberOrders:
- В блоке GET /orders/> задается использование данных в переменной. В конфигурации (либо Add Variables) необходимо заполнить таблицу:
Varable
Current Value
(переменная, в которую подставляем значение)
(значение, которое будет подставлено в переменную number)
Мы реализовали простейший сценарий, при выполнении первого запроса создается заявка, и в ответе мы получаем ее номер (number). Далее мы извлекаем из ответа номер заявки и переиспользуем его в следующем запросе.
Таким образом, блоки Flow предоставляют множество функций, которые сложно реализовать с помощью кода.
Визуализация ответов
Postman предоставляет программируемый способ визуального представления ответов на запросы. Код визуализации, добавленный в «tests» для запроса, будет отображаться на вкладке «Visualize».
Для визуализации ответа, в Postman используется метод pm.visualizer.set() . Метод принимает три параметра:
- layout (обязательно) — это строка HTML-шаблона Handlebars;
- data (необязательно) — это данные, которые вы можете привязать к шаблону. Доступ к свойствам этого объекта можно получить в шаблоне;
- options (необязательно) — это options объект для Handlebars.compile(). Вы можете использовать это, чтобы контролировать, как Handlebars компилирует шаблон.
Визуализируем ответ в виде таблицы. Допустим, у нас есть GET запрос, в ответе мы получаем JSON с множеством параметров:
Во вкладке tests выполним следующее:
- Сформируем строку HTML-шаблона.
var template = ` .tftable .tftable th .tftable tr .tftable td .tftable tr:hover Пользователь МРФ Тип работы
- Выполним циклический опрос JSON на наличие параметров:
> > onClick="handleClick(this.id)"> >>> > > > `;
- Задействуем метод визуализации:
pm.visualizer.set(template, );
Визуализация ответа выполнена. Нажатием правой кнопки мыши на таблицу можно открыть консоль разработчика для редактирования HTML.
Визуализация предоставляет возможность работы с данными ответа в удобном виде. Так, например, можно использовать код HTML (скопированный через консоль разработчика) в Confluence. Для этого необходимо на странице Confluence разместить макрос HTML и вставить в рамку скопированный код HTML. Данный макрос выполнит HTML код при открытии страницы. Confluence распознает таблицу и даже позволит воспользоваться сортировкой, фильтрами и экспортом в различные форматы.
Вместо выводов
Я описал лишь малую часть интересной функциональности Postman. Мне бы хотелось, чтобы данная статья показала вам, что Postman может быть не только обычным инструментом для тестирования API, но и многофункциональной платформой, способной решать различные задачи в ходе работы с интеграционным слоем.
- Блог компании Ростелеком
- Тестирование IT-систем
- API
- Тестирование веб-сервисов
Mock-сервисы для тестирования: How to use + Quick start
В подавляющем большинстве этот термин знаком всем и его сущность ни для кого не является секретом. Но, по традиции, все же лучше начать с определения:
13K открытий
Заглушка — это небольшая часть кода, которая заменяет собой другой компонент во время тестирования. Преимущество использования заглушки заключается в том, что она возвращает последовательные результаты, упрощая написание теста. Тесты можно выполнять, даже если другие компоненты пока не работают.
В тестировании мы используем заглушки для имитации работы внешней системы. Заглушка приходится как нельзя кстати, когда мы ожидаем получить конкретный ответ на конкретный запрос. И вместо того, чтобы на самом деле отправлять этот запрос вовне, мы подкладываем нужный нам ответ.
На хабре достаточно статей по данной тематике. Наш QA-инженер–Николай – хочет остановиться на кратком руководстве для старта с общим обзором и примерами по одним из самых популярных инструментов. Дабы была возможность быстро определить перечень используемых инструментов и в дальнейшем продолжить уже их более углубленное изучение.
В данной статье будут приведены примеры с локальным развертыванием. Этот вариант идеально подойдет, если тестируемое приложение развернуто на рабочей машине. Если же стоит задача развернуть заглушку для удаленного сервиса, тут лучше обратиться к девопсам/администраторам. Т.к. политика безопасности не всегда дает доступ на сервер с крутящимся на нем приложением, а перед тем как будущая заглушка начнет отрабатывать – сервис придется перенастроить и перезагрузить. Иными словами сломать. Еще ни одного тестировщика за такие фокусы не похвалили. Потому в данной статье этот вопрос подниматься не будет.
Немного теории
Существует несколько видов объектов, которые позволяют симулировать поведение реальных объектов во время тестирования:
- Dummy — пустые объекты, которые передаются в вызываемые методы, но не используются. Предназначены лишь для заполнения параметров методов.
- Fake — объекты, которые имеют реализации, но в таком виде, который делает их неподходящими для использования в рабочей ситуации.
- Stub — предоставляют заранее заготовленные ответы на вызовы во время теста и не отвечают ни на какие другие вызовы, которые не требуются в тесте.
- Mock — объекты, которые заменяют реальный объект в условиях теста и позволяют проверять вызовы своих методов. Содержат заранее подготовленные описания вызовов, которые они ожидают получить. Применяются в основном для тестирования поведения пользователя.
Нас интересуют последние два вида, т.к. в тестировании мы и занимаемся тем, что имитируем и эмулируем работу реальных пользователей.
Старый и всем давно известный инструмент. Пригоден как для тестирования SOUP, так и REST сервисов, автоматизации их проверок и создания заглушек.
Для тех, кто все еще путается:
REST и SOAP не сопоставимы!
— REST — это архитектурный стиль, оперирующий JSON через HTTP.
— SOAP — это формат обмена XML сообщениями с ограничениями по структуре сообщений через HTTP.
Рассмотрим на примере REST сервиса. Для SOAP шаги будут идентичными и не вызовут особых трудностей.
При первом запуске автоматически всплывает окно Endpoint Explorer, в котором необходимо указать сам запрос и необходимые для работы заголовки:
После пробного запроса получаем ошибку, что такой ресурс не существует. Сюда же можно отнести случай, когда сервис есть, но возвращает не совсем то, что нужно:
Использование mock при интеграции с внешним API
На Infostart Meetup DevOps инженер-программист Андрей Крапивин поделился с коллегами опытом тестирования интеграции с внешним API – показал возможности мокирования и рассмотрел их применение на реальном примере тестирования погодного виджета для конфигурации «Бухгалтерия 3.0».
Тестирование интеграции с API
Задача интеграции с API встречается очень часто, по популярности она уступает только обмену между базами и выгрузке из Excel. Есть много разных вариантов API:
- Есть мастодонты – Google, Яндекс – у них много разных сервисов, с которыми можно интегрироваться. Например, получение переводов, генерация и чтение текстов, прогнозы погоды.
- Часто приходится интегрироваться с «Почтой России» или OZON.RU – если ваша компания занимается перевозками или отправкой товаров.
- С Telegram – если надо отправлять или получать данные и писать ботов.
- И, конечно, наши государственные сервисы: например, ФНС, если вам нужно получать данные о контрагентах.
У всех этих вариантов есть нечто общее:
- Клиент-серверное взаимодействие, когда вы от себя отправляете данные, а все обрабатывается на стороне сервиса.
- Сервер не будет хранить ваше состояние – ему без разницы, какой вы запрос отправите, состояние запроса вы должны соблюдать самостоятельно.
- Используется немного кэширования.
- Формат ответа – обычно HTML, XML, JSON либо бинарные файлы (если вы что-то получаете в виде картинок или pdf-документов).
Проблемы тестирования
Какие проблемы возникают в тестировании с API?
- Основное – стабильность тестов, потому что когда у вас внешнее API – вы целиком и полностью зависите от того, что происходит на сервере. Сервис может уйти на обслуживание, не выдержать нагрузки и, соответственно, ваши тесты тоже перестают проходить.
- Возможность покрытия сценариев. Тяжело реализовать все сценарии, которые вы можете придумать, когда у вас внешнее API.
- Большая проблема – скорость ответов. Зависит от того, какая нагрузка на сервере, которым вы пользуетесь, сколько до вас идет ответ.
- Следующая проблема – риск работы с реальными данными. Если вы тестируете наживую, то можете пересечься с вашими реальными данными, которыми оперирует учетная система. Ничем хорошим это не закончится.
- Еще есть проблема: можно увлечься, и вместо того, чтобы тестировать, как ваше приложение взаимодействует с API, начать тестировать API. Вы думаете: «А что будет, если я отправлю такой запрос, а если такой, а если здесь пробел поставлю?» И получается, что вместо того, чтобы придумать сценарий, как вы будете взаимодействовать с сервером, вы смотрите, как ответит сервер.
Методы решения проблем
Какие есть методы решения проблем?
Самый простой вариант – вам предоставят тестовый стенд. Вам выделяют отдельную область, в которой вы будете тестировать свои алгоритмы.
В чем тут может быть проблема? Если стенд один, а разработчиков несколько – ваши тесты могут мешать друг другу.
Следующий вариант – написать свой сервер.
Все программисты, если что-то не работает на стороне, начинают писать свое. Делаем небольшой сервер – берем Python или OneScript.Web – и пишем небольшие заглушки. Мы знаем, какие запросы отправляем, готовим под них ответы.
В чем здесь проблема? Его надо писать. Это будет ваше отдельное приложение, которое надо поддерживать.
Следующий вариант – использовать специальные mock-сервера (заглушки).
Для них все равно нужно будет готовить данные.
При этом вам нужно не забывать за этими данными следить – вы поставили заглушку, у вас тестирование прекрасно проходит, но если на «боевой» уже поменялся API, и у вас тестируется не реальная ситуация.
Mock-сервер можно использовать только для тестов. Никаких альфа-бета-тестирований на mock-сервере проводить не стоит – для этого нужно использовать только боевой контур.
Последний вариант – решить все архитектурно, написать приложение так, чтобы оно само в себе содержало тестовые данные и могло их в любой момент использовать. Это самый сложный вариант, мы не будем на нем останавливаться
Мы разберем мокирование.
Мокирование
Мокирование от английского mock – «заглушка». Выглядит это следующим образом.
- В первую очередь, тест готовит себе данные – обращается к специально запущенному приложению, передает ему информацию о том, что собирается отправить в него такие-то данные и ожидает получить на них такие-то ответы.
- Далее тест обращается к вашей системе, чтобы она отправила запросы не на реальный сервер, а на заглушку.
- Заглушка присылает ответы, а тест верифицирует: правильные ли данные получила система.
Вариантов таких мок-серверов много, перечислю самые популярные.
- Один из первых, который мне попадался – это Postman, в нем есть возможность мокирования. Postman – одно из мастхэв-решений, если вы пишете интеграцию. Работать с запросами в нем очень удобно. И также в нем есть собственная возможность мокирования. Достаточно слабенькая, но есть.
- JSON-Server – легкое решение, в нем есть база данных, основанная на JSON. Он умеет сохранять данные и преобразовывать.
Но я вам хочу рассказать про работу с WireMock.
WireMock – запуск, возможности, управление
Что такое WireMock?
- WireMock – приложение на Java, что уже хорошо. Значит, вы сможете его запускать и на Windows, и на Linux.
- Он умеет работать в режиме JUnit, когда вы пишете тесты на Java, и Standalone – как отдельно запускаемое приложение. С 1С я его чаще использую как Standalone.
- Он очень хорошо управляется через HTTP-запросы. Когда вы его как Standalone подняли, он управляется, отправляя запросы на нужный адрес.
- Все настройки он хранит в JSON-формате – это очень удобно, потому что JSON хорошочитаемый, его удобно смотреть при code-review.
- У него есть функция записи и воспроизведения – чуть позже расскажу, что это такое.
- Также он умеет назначать приоритеты шаблонам, умеет работать с состояниями и симулировать ошибки.
Чтобы было понятнее – давайте разберем конкретную задачу.
Практика
Наша задача – сделать погодный виджет для Бухгалтерии 3.0. Я достаточно часто встречал такие обработки на Инфостарте – видимо, многие клиенты хотят знать погоду, не выходя из 1С.
Мы выведем на главной странице Бухгалтерии 3.0 текущий прогноз погоды и постараемся максимально закрыть это тестами.
Реализация выглядит вот так – это расширение, которое я нашел на Инфостарте в публикации //infostart.ru/public/801039/. Оно выводит погоду – сейчас +18 градусов.
Давайте накроем его код тестом.
Тест делаем как обычно, кнопконажималкой – запускаем 1С, получаем нужный элемент и проверяем его данные.
Насколько это будет стабильный тест? С утра у нас было +7, днем стало +15, два часа назад пошел дождь, но теперь светит солнце. Соответственно, тест будет совершенно нестабилен – каждый раз, когда меняется погода, тест будет падать.
Как раз его мы и попробуем замокировать.
Подготовка
Первым делом, нужно провести подготовку.
- Настроить запуск 1С в режиме тестирования.
- Научиться перенаправлять запросы – чтобы запросы отправлялись не к внешнему сервису, а к нашему.
- Подготовить набор ответов.
Начнем с запуска в режиме тестирования. Я обычно реализую такой метод через параметр сеанса, который зависит от параметра запуска.
Добавляем параметр запуска —usemock, и в случае, если система запущена с этим параметром, говорим, что вместо запроса конкретному серверу будем переходить на наш, подготовленный.
Теперь насчет подготовки мокированных ответов.
У WireMock есть специальноеприложение – WireMockUI, которое позволяет настроить следующую вещь: мы в нем указываем, какой сервис API мы хотим мокировать, на какой сервер мы его перенаправляем.
Дальше мы его запускаем в режиме записи. Можем сделать все, что нам нужно в 1С или приложении. WireMock перехватывает эти запросы и готовит необходимые файлики ответов и шаблоны для запросов. Мы это забираем и запускаем с уже заготовленными шаблонами.
Вот так это выглядит. Мы включили запись, что-то потыкали, получили нужные ответы, забираем эти результаты себе. Складываем это в какую-то папку с проектом или еще куда. И после этого запускаем WireMock с указанием того, что все наши шаблоны лежат в нужной папке.
Шаблон выглядит вот так: здесь URL запроса и ответ.
Содержание большого JSON-ответа можно упаковать в отдельный файлик – его адрес будет указан в свойства bodyFileName.
Вот так выглядит этот JSON-файл ответа.
Теперь мы можем переписать наш тест следующим образом – указываем, где у нас живет WireMock со всеми настройками, запускаем его. По итогу тест получается стабильным. Сколько бы раз мы его ни запускали, у нас получится одна и та же температура на главной странице.
Макеты
Давайте подробнее разберемся, что такое макет.
Макет – это шаблон, по которому будет определяться, откуда брать ответ. Он должен описать вашу строку запуска, включая переданные в нее параметры и заголовки.
Самое первое, что есть в шаблонах – какой HTTP-метод мы используем.
Например, можно указать, что этот шаблон будет использоваться для обработки всех запросов на такой-то адрес, которые используют метод GET.
Дальше мы разбираем, куда уходит наш запрос.
- Первый вариант – использовать свойство url, которое описывает полное соответствие, включая все параметры. Мы прямо говорим, что если мы делаем GET-запрос по такому-то URL – возвращай ответ. Это не очень удобно, потому что в коде могут поменяться местами параметры, некоторые параметры будут являться обязательными или необязательными.
- Поэтому следующий вариант – в свойстве urlPattern написать регулярное выражение, которое будет разбирать вашу строку.
- И совсем простой вариант – с помощью свойства urlPath указывать только начальный путь. Чаще всего я использую именно этот вариант, когда указываю, что запрос будет по такому-то URL, а потом отдельно обрабатываю параметры запроса через свойство queryParameters.
Есть несколько вариантов обработки параметров:
- equalTo – это точное соответствие, когда вы говорите, чему должен быть равен этот параметр;
- matches – регулярное выражение, которому этот параметр должен соответствовать.
Если параметр необязательный, его все равно нужно указать. Ведь если запрос придет с этим параметром, то шаблон в этом случае уже не сработает.
С помощью свойства bodyPatterns есть возможность обрабатывать тело запроса. В зависимости от того, какой запрос вы обрабатываете, можно использовать:
- XPath-язык – свойство matchesXPath;
- JSONPath – свойство matchesJsonPath;
- или описывать полное совпадение тела запроса через свойства equalToXml и equalToJson.
Это все, что касается шаблона запроса.
Дальше этот шаблон переадресовывает к шаблонам ответа. То есть, для каждого шаблона на запрос мы готовим ответ.
Вы можете готовить динамические ответы:
- указывать дату в определенном формате;
- разбирать входящие параметры, если это требуется;
- там есть конкатенация строк и т.д.
Но динамические шаблоны ответа – это не очень хорошая практика, потому что, по-хорошему, вы должны готовить шаблоны под каждый тест.
Stateful
Разберемся, что делать, если у нас есть зависимость от состояния. Сервер не хранит состояния обмена по API, но бэкэнд может какие-то состояния хранить.
Например, если у вас сервис учитывает контроль остатков. Вы делаете запрос остатков, списываете товар, делаете еще один запрос – у вас товар должен закончиться. Получается, у вас есть несколько состояний.
WireMock позволяет это реализовать:
- у него есть понятие сценария;
- каждый сценарий должен находиться в определенной стадии;
- эти стадии можно переключать как прямым запросом по HTTP, так и самим сценарием.
Например, здесь на слайде указан сценарий «To do list», и текущее состояние у него стартовое (“requiredScenarioState”: “Started”).
Давайте реализуем такой контроль состояний у нас – чтобы для разных состояний за окном погода на виджете менялась.
Нам потребуется два шаблона:
- один стартовый (“requiredScenarioState”: “Started”),
- другой я назвал bad (“requiredScenarioState”: “bad”) – сейчас объясню, почему.
Логика такая – когда наш сервер стартует, он находится в стартовом состоянии и подает нужный ответ.
А когда мы переключим его в новое состояние, данные на виджете должны измениться.
Как это будет выглядеть в тесте? Мы указываем, что работаем со сценарием Weather.
Прямо в коде явно переключаем его состояние – тогда через 30 секунд у нас меняется погода на виджете.
Аварийные ситуации – низкая скорость, обрыв, мусор
Плохо, когда пользователю показывают сообщения об ошибке. Они чаще всего пугаются, их закрывают и не сознаются, что у них были ошибки вообще.
Лучше постараться обработать эти аварийные ситуации – повторно отправить запрос или подсказать пользователю.
Но когда вы работаете с реальным API, с тем же тестовым стендом, протестировать низкую скорость или обрыв соединения – большая проблема. Проблема даже вручную это протестировать: наш тестировщик это проделывал, выключая вай-фай во время теста.
Как это можно реализовать с помощью мокирования?
Первое – задержка ответа. Прямо в шаблоне можно указать, что нам потребуется какая-то задержка.
- Она бывает фиксированная, когда вы говорите, что каждый запрос должен быть задержан на 1000 миллисекунд, на 3 минуты.
- Есть вариант показать, что это уже случайная величина на каждый запрос.
- И есть интересный вариант – цепочка ответов, когда ваш запрос идет с задержкой в 1000 миллисекунд пятью пачками (каждые 200 миллисекунд отправляет пачку ответов) и в конце концов собирается. На слайде показано, как это может быть реализовано.
Еще интересный вариант, что делать, если вам пришел вообще невалидный ответ сервера:
- вам может прийти пустой запрос;
- может прийти мусор в ответе – когда вначале был json, а потом абракадабра;
- и соединение может быть закрыто во время получения данных.
На слайде показано, как выглядит запрос Postman, когда приходит совсем пустой ответ – он не может ничего прочитать.
Если вы перехватите такую ошибку, 1С это более-менее переваривает.
Интересный вариант реализации – как протестировать обрыв связи. Реализуется как раз цепочками состояний.
- есть стартовое состояние – Started;
- дальше мы получаем валидное состояние с погодой – Good;
- переключаем наш сценарий запросом CONNECTION_RESET_BY_PEER на состояние Conn_reset («Оборвать соединение»);
- и снова переключаем на хорошее – Good.
Таким образом проверяем, что в случае обрыва соединения будет попытка его восстановить, и пользователю будет выведено сообщение.
Резюме
Как итог всего этого – вы получаете:
- стабильную проверку позитивных и негативных сценариев, не угрожая продуктовому серверу;
- вполне параллельно тестируете локально – тестировщики тестируют у себя, одновременно гоняются тесты на сервере, все замечательно;
- также можете проверять неадекватные ситуации – сбои, менять ответы, если что-то пошло не так, мусор в ответы наваливать.
Все это позволит вам повышать качество ваших продуктов.
Полезные ссылки
Накину чуть-чуть полезных ссылок, что можно почитать подробнее:
- Статья https://code.tutsplus.com/ru/tutorials/fake-rest-api-up-and-running-using-json-server—cms-27871, которая описывает работу с маленьким интересным сервисом JSON-Server.
- Статья на Инфостарте //infostart.ru/public/1014870/ про использование SoapUI в качестве заглушек.
- Документация на WireMock http://wiremock.org/docs/
Проекты на GitHub
- Виджет «Погодка», который я использовал https://github.com/petypen/Pogogka20
- Мой демо-стенд: https://github.com/KrapivinAndrey/infostart2020-DevOps1c-Mockdemo – можете зайти и посмотреть все сценарии, которые я вам показал. Плюс еще парочка реализованных. Возьмите и попробуйте их локально позапускать, посмотреть, как это все устроено.
Вопросы
Где взять шаги для фич, чтобы работать с WireMock?
Шаги можно взять там же, у меня в репозитории
Пробовали ли вы использовать OneScript.Web в качестве мок-сервера?
Как я уже сказал, когда пишешь сам, проблема в том, что нужно писать. Первый сценарий мы реализовали на Python, потом переписали на OneScript.Web. Но когда начинается усложнение системы, ты понимаешь, что у тебя есть твой код, теперь тебе еще нужно поддерживать свой мок-сервер. Зачем, если уже люди постарались и написали готовое решение.
Как изменяется состояние сервера? Это какая-то опция в командной строке его запуска?
Во-первых, нужно создать шаблон ответа для этой стадии при этом имени сценария. Само состояние меняется сценарием – там есть шаг, который напрямую направляет запрос к WireMock и меняет стадию. И при выполнении запроса есть опция, что этот запрос переключает на следующую стадию.
Непонятно, в каком формате смотреть результаты тестов.
Результаты тестов смотреть в привычном формате – Allure, Junit. Как обычно, мы проверяем результаты нашего тестирования в фреймворке тестирования – например, Vanessa ADD. Мы не тестируем API, мы тестируем поведение нашей системы – мы отправляем запрос, ожидаем, что поле будет иметь такое значение. Если это не так, очевидно, что тест провален.
Данная статья написана по итогам доклада (видео), прочитанного на онлайн-митапе «DevOps в 1С: Тестирование и контроль качества решений на 1С».
30 мая — 1 июня 2024 года состоится конференция Анализ & Управление в ИТ-проектах, на котором прозвучит 130+ докладов.
Темы конференции:
- Работа с данными: архитектура, интеграции, отчеты.
- Программная инженерия.
- Решения 1С: архитектура, учет и практические задачи.
- Управление проектом.
- Управление продуктом.
- Soft skills, управление командой проекта.
- Кейсы автоматизации на 1С
Конференция для аналитиков и руководителей проектов, а также других специалистов из мира 1С, которые занимаются системным и бизнес-анализом, работают с требованиями, управляют проектами и продуктами!
Мок-сервер для автоматизации мобильного тестирования
Работая над последним проектом, столкнулся с тестированием мобильного приложения, связанного на уровне бизнес-логики с различными сторонними сервисами. Тестирование этих сервисов не входило в мою задачу, однако проблемы с их API блокировали работу по самому приложению – тесты падали не из-за проблем внутри, а из-за неработоспособности API, даже не доходя до проверки нужной функциональности.
Традиционно для тестирования таких приложений используются стенды. Но они не всегда работают нормально, и это мешает работе. В качестве альтернативного решения я использовал моки. Об этом тернистом пути и хочу рассказать сегодня.
Чтобы не трогать код реального проекта (под NDA), для наглядности дальнейшего изложения я создал простой REST-клиент под Android, позволяющий отправлять на некий адрес HTTP-запросы (GET/POST) с необходимыми мне параметрами. Его-то мы и будем тестировать.
Код приложения-клиента, диспатчеров и тестов можно скачать с GitLab.
Какие существуют варианты?
Подходов к мокированию в моем случае существовало два:
- развернуть мок-сервер в облаке или на удаленной машине (если речь идет о конфиденциальных разработках, которые нельзя выносить в облако);
- запускать мок-сервер локально – прямо на телефоне, на котором тестируется мобильное приложение.
Выбор мок-сервера
Разных инструментов существует много. Я пытался работать с несколькими и почти в каждом столкнулся с определенными проблемами:
- Mock-server, wiremock – два мок-сервера, которые я так и не смог нормально запустить на Android. Поскольку все эксперименты происходили в рамках живого проекта, время на выбор было ограничено. Поковырявшись с ними пару дней, я оставил попытки.
- Restmock – это обертка над okhttpmockwebserver, подробнее о котором речь пойдет далее. Выглядела она неплохо, запустилась, но разработчик этой обертки спрятал “под капотом” возможность задания IP-адреса и порта мок-сервера, а для меня это было критично. Restmock стартовал на каком-то случайном порту. Ковыряясь в коде, я увидел, что при инициализации сервера разработчик использовал метод, который задавал порт случайным образом, если не получал его на вход. В принципе, можно было наследоваться от этого метода, но проблема была в приватном конструкторе. В итоге от обертки я отказался.
- Okhttpmockwebserver – попробовав разные инструменты, я остановился на мок-сервере, который нормально собрался и запустился локально на устройстве.
Разбираем принцип работы
Текущая версия okhttpmockwebserver позволяет реализовать несколько сценариев работы:
- Очередь ответов. Ответы мок-сервера складываются в очередь, работающую по принципу FIFO. Неважно, к какому API и по какому пути я буду обращаться, мок-сервер по очереди будет выкидывать сообщения, заложенные в эту очередь.
- Диспатчер позволяет создать правила, определяющие, какой ответ отдавать. Допустим, запрос пришел по URL, содержащему некий путь, например /get-login/. По этому /get-login/ мок-сервер и отдает единичный, заранее заданный ответ.
- Request Verifier. Опираясь на предыдущий сценарий, я могу проверять запросы, которые отправляет приложение (что в заданных условиях запрос с определенными параметрами действительно уходит). При этом ответ неважен, поскольку он определяется тем, как работает API. Этот сценарий и реализует Request verifier.
Очередь ответов
Простейшая реализация мок-сервера – очередь ответов. До теста я определяю адрес и порт, где будет развернут мок-сервер, а также тот факт, что он будет работать по принципу очереди из сообщений – FIFO (first in first out).
Далее запускаю мок-сервер.
class QueueTest: BaseTest() < @Rule @JvmField var mActivityRule: ActivityTestRule= ActivityTestRule(MainActivity::class.java) @Before fun initMockServer() < val mockServer = MockWebServer() val ip = InetAddress.getByName("127.0.0.1") val port = 8080 mockServer.enqueue(MockResponse().setBody("1st message")) mockServer.enqueue(MockResponse().setBody("2nd message")) mockServer.enqueue(MockResponse().setBody("3rd message")) mockServer.start(ip, port) >@Test fun queueTest() < sendGetRequest("http://localhost:8080/getMessage") assertResponseMessage("1st message") returnFromResponseActivity() sendPostRequest("http://localhost:8080/getMessage") assertResponseMessage("2nd message") returnFromResponseActivity() sendGetRequest("http://localhost:8080/getMessage") assertResponseMessage("3rd message") returnFromResponseActivity() >>
Тесты написаны с помощью фреймворка Espresso, предназначенного для исполнения действий в мобильных приложениях. В этом примере я выбираю типы запросов и отправляю их по очереди.
После запуска теста мок-сервер дает ему ответы в соответствии с прописанной очередью, и тест проходит без ошибок.
Реализация диспатчера
Диспатчер – это набор правил, по которым работает мок-сервер. Для удобства изложения я создал три разных диспатчера: SimpleDispatcher, OtherParamsDispatcher и ListingDispatcher.
SimpleDispatcher
Для реализации диспатчера okhttpmockwebserver предоставляет класс Dispatcher() . От него можно наследоваться, переопределив функцию dispatch по-своему.
class SimpleDispatcher: Dispatcher() < @Override override fun dispatch(request: RecordedRequest): MockResponse < if (request.method == "GET")< return MockResponse().setResponseCode(200).setBody("""< "message": "It was a GET request" >""") > else if (request.method == "POST") < return MockResponse().setResponseCode(200).setBody("""< "message": "It was a POST request" >""") > return MockResponse().setResponseCode(200) > >
Логика в этом примере простая: если приходит GET, я возвращаю сообщение, что это GET request. Если POST, возвращаю сообщение о POST request. В иных ситуациях возвращаю пустой запрос.
В тесте появляется dispatcher – объект класса SimpleDispatcher , который я описал выше. Далее, как и в предыдущем примере, запускается мок-сервер, только на этот раз указывается своего рода правило работы с этим мок-сервером – тот самый диспатчер.
Исходники тестов с SimpleDispatcher можно найти в репозитории.
OtherParamsDispatcher
Переопределяя функцию dispatch , я могу оттолкнуться от других параметров запроса для отправки ответов:
class OtherParamsDispatcher: Dispatcher() < @Override override fun dispatch(request: RecordedRequest): MockResponse < return when < request.path.contains("?queryKey=value") ->MockResponse().setResponseCode(200).setBody("""< "message": "It was a GET request with query parameter queryKey equals value" >""") request.body.toString().contains("\"bodyKey\":\"value\"") -> MockResponse().setResponseCode(200).setBody("""< "message": "It was a POST request with body parameter bodyKey equals value" >""") request.headers.toString().contains("header: value") -> MockResponse().setResponseCode(200).setBody("""< "message": "It was some request with header equals value" >""") else -> MockResponse().setResponseCode(200).setBody("""< Wrong response >""") > > >
В данном случае я демонстрирую несколько вариантов условий.
Во-первых, в API можно передавать параметры в адресной строке. Поэтому я могу поставить условие на вхождение в path какой-либо связки, например “?queryKey=value” .
Во-вторых, данный класс позволяет залезть внутрь тела (body) запросов POST или PUT. Например, можно использовать contains , предварительно выполнив toString() . В моем примере условие срабатывает, когда приходит POST-запрос, содержащий “bodyKey”:”value” . Аналогично я могу валидировать header запроса ( header : value ).
За примерами тестов рекомендую обратиться к репозиторию.
ListingDispatcher
При необходимости можно реализовать и более сложную логику – ListingDispatcher. Тем же способом я переопределяю функцию dispatch . Однако теперь прямо в классе задаю дефолтный набор стабов ( stubsList ) – моков на разные случаи жизни.
class ListingDispatcher: Dispatcher() < private var stubsList: ArrayList= defaultRequests() @Override override fun dispatch(request: RecordedRequest): MockResponse = try < stubsList.first < it.matcher(request.path, request.body.toString()) >.response() > catch (e: NoSuchElementException) < Log.e("Unexisting request path /get", "queryParam=value", "", """< "message" : "Request url starts with /get url and contains queryParam=value" >""")) allStubs.add(RequestClass("/post", "queryParam=value", "", """< "message" : "Request url starts with /post url and contains queryParam=value" >""")) allStubs.add(RequestClass("/post", "", "\"bodyParam\":\"value\"", """< "message" : "Request url starts with /post url and body contains bodyParam:value" >""")) return allStubs > fun replaceMockStub(stub: RequestClass) < val valuesToRemove = ArrayList() stubsList.forEach < if (it.path.contains(stub.path)&&it.query.contains(stub.query)&&it.body.contains(stub.body)) valuesToRemove.add(it) >stubsList.removeAll(valuesToRemove) stubsList.add(stub) > fun addMockStub(stub: RequestClass) < stubsList.add(stub) >>
Для этого я создал открытый класс RequestClass , все поля которого по умолчанию пустые. Для данного класса я задаю функцию response , которая создает объект MockResponse (возвращающую ответ 200 или некий иной responseText ), и функцию matcher , возвращающую true или false .
open class RequestClass(val path:String = "", val query: String = "", val body:String = "", val responseText: String = "")
В результате я могу строить более сложные сочетания условий для стабов. Мне эта конструкция показалась более гибкой, хотя принцип в ее основе очень простой.
Но более всего в этом подходе мне понравилось, что я могу подставлять какие-то стабы прямо на ходу, если появилась необходимость что-то поменять в ответе мок-сервера на одном тесте. При тестировании больших проектов такая задача возникает довольно часто, например при проверке каких-то специфических сценариев.
Замену можно осуществить следующим образом:
fun replaceMockStub(stub: RequestClass) < val valuesToRemove = ArrayList() stubsList.forEach < if (it.path.contains(stub.path)&&it.query.contains(stub.query)&&it.body.contains(stub.body)) valuesToRemove.add(it) >stubsList.removeAll(valuesToRemove) stubsList.add(stub) >
При такой реализации диспатчера тесты остаются простыми. Я также стартую мок-сервер, только выбираю ListingDispatcher .
class ListingDispatcherTest: BaseTest() < @Rule @JvmField var mActivityRule: ActivityTestRule= ActivityTestRule(MainActivity::class.java) private val dispatcher = ListingDispatcher() @Before fun initMockServer() < val mockServer = MockWebServer() val ip = InetAddress.getByName("127.0.0.1") val port = 8080 mockServer.setDispatcher(dispatcher) mockServer.start(ip, port) >. . . >
Ради эксперимента я заменил стаб на POST:
@Test fun postReplacedStubTest() < val params: HashMap= hashMapOf("bodyParam" to "value") replacePostStub() sendPostRequest("http://localhost:8080/post", params = params) assertResponseMessage("""< "message" : "Post request stub has been replaced" >""") >
Для этого вызвал функцию replacePostStub от обычного dispatcher и добавил новый response .
private fun replacePostStub() < dispatcher.replaceMockStub(RequestClass("/post", "", "\"bodyParam\":\"value\"", """< "message" : "Post request stub has been replaced" >""")) >
В тесте выше я проверяю, что стаб был заменен.
Затем я добавил новый стаб, которого не было в дефолтных.
@Test fun getNewStubTest() < addSomeStub() sendGetRequest("http://localhost:8080/some_specific_url") assertResponseMessage("""< "message" : "U have got specific message" >""") >
private fun addSomeStub() < dispatcher.addMockStub(RequestClass("/some_specific_url", "", "", """< "message" : "U have got specific message" >""")) >
Request Verifier
Последний кейс – Request verifier – обеспечивает не мокирование, а проверку отправляемых приложением запросов. Для этого я точно так же стартую мок-сервер, реализовав диспатчер, чтобы приложение возвращало хоть что-то.
При отправке запроса из теста тот приходит в мок-сервер. Через него я могу получить доступ к параметрам запроса, используя takeRequest() .
@Test fun requestVerifierTest() < val params: HashMap= hashMapOf("bodyKey" to "value") val headers: HashMap = hashMapOf("header" to "value") sendPostRequest("http://localhost:8080/post", headers = headers, params = params) val request = mockServer.takeRequest() assertEquals("POST", request.method) assertEquals("value", request.getHeader("header")) assertTrue(request.body.toString().contains("\"bodyKey\":\"value\"")) assertTrue(request.path.startsWith("/post")) >
Выше я показал проверку на простом примере. Точно такой же подход можно использовать для сложных JSON, в том числе для проверки всей структуры запроса (можно сравнивать на уровне JSON или распарсить JSON на объекты и проверить равенство на уровне объектов).
Итоги
В целом инструмент (okhttpmockwebserver) мне понравился, и я использую его на большом проекте. Безусловно, есть некоторые мелочи, которые я хотел бы изменить.
Например, мне не нравится, что приходится стучаться по локальному адресу (localhost:8080 в нашем примере) в конфигах своего приложения; возможно, я еще найду способ все настроить так, чтобы мок-сервер отвечал при попытке отправить запрос на любой адрес.
Также мне не хватает возможности переадресации запросов – когда мок-сервер отправляет запрос дальше, если у него нет для него подходящего стаба. В данном мок-сервере такого подхода нет. Впрочем, до их внедрения и не дошло, поскольку на данный момент в “боевом” проекте не стоит такой задачи.
Автор статьи: Руслан Абдулин
P.S. Мы публикуем наши статьи на нескольких площадках Рунета. Подписывайтесь на наши страницы в VK, FB или Telegram-канал, чтобы узнавать обо всех наших публикациях и других новостях компании Maxilect.
- mock
- mockserver
- okhttpmockwebserver
- тестирование
- тестирование по
- тестирование мобильных приложений
- espresso
- мок
- Блог компании Maxilect
- Тестирование IT-систем
- API
- Тестирование мобильных приложений