Что такое mock сервис в soapui
Создание в SoapUI асинхронного REST MockService с запуском в Portainer
Задача
Разработать мок для проверки асинхронного обмена сообщениями с внешней системой.
Как пример, рассмотрим некий кейс проверки валидности промокода внешней системой. По шагам:
1) Отправляем запрос в сервис внешней системы;
Запрос PUT содержит поля (например, в headers):
correlationId=рандомная строка, которую нужно будет вернуть в callback
replyTo= адрес сервиса принимающего callback
3) Через 5 секунд внешняя система отправляет асинхронный callback (по адресу replyTo из запроса):
Запрос POST с телом сообщения:
correlationId = correlationId из запроса
Решение
Для решения данной задачи используем SoapUI. Данный инструмент достаточно гибок в плане создания «умных» моксервисов, тк позволяет написать скрипты, как на этапах запуска/остановки моксервиса (Start Script/ Stop Script), так и перед/после получения запроса (OnRequest Script/AfterRequest Script).
Начнем:
1) В SoapUI создать новый проект, например, «test-soapui-project» (либо выбрать существующий) и создать для него два REST MockService.
в Path произвольный путь к моксервису, например, «/SP/ValidationSystem»,
в Port значение порта на котором будет запущен мок,
в Host значение «localhost».
Аналогично в Path произвольный путь к моксервису, например, «/SP/SpHost»
2) В мок Validation REST MockService добавить:
в Resource Path произвольный путь к методу внутри моксервиса, например, «/validation»
в Method указываем метод Rest запроса, в нашем случае PUT (шаг 1 в задаче)
для запроса PUT добавить ответ 204 No content
в Content | Media type тип ответа на запроса, в нашем случае «application/json».
переменные в Properties:
это переменные в которые будут сохранены поля promotionalCode, replyTo, correlationId из поступившего запроса PUT.
*log.info можно не использовать, носит информативный характер для проверки хода выполнения операций на вкладке script log.
3) В мок Validation Receiving CALLBACK REST MockService добавить:
в Resource Path произвольный путь к методу внутри моксервиса, например, «/callback»
в Method указываем метод Rest запроса, в нашем случае POST.
для запроса POST добавить ответ 200 OK
в Content | Media type тип ответа на запроса, в нашем случае «application/json».
4) Запустить оба моксервиса
5) Проверяем работу моксервисов
Отправляем запрос проверки валидации, например, через Postman:
Выполняем PUT вызов метода validation на Мок 1 (Validation REST MockService), указывая в Headers 3 параметра, как описано в Задаче
Запуск моксервиса в Portainer:
1) В images добавляем образ для работы с моксервисами SoapUI:
находим на docker hub необходимый образ. в нашем случае fbascheper/soapui-mockservice-runner (https://hub.docker.com/r/fbascheper/soapui-mockservice-runner/)
переходим в Portainer в раздел images
по названию загружаем необходимый образ
2) Разворачиваем стек для работы с моксервисами :
Составить Compose file:
*в нашем примере проект soapUI разместили на сервере в /soapui/soapui-prj/
перейти в Portainer в раздел Stacks,
добавить новый стек:
— указать в поле Name, как будет назван наш стек, например, sp-soapui-mocks,
— в Web editor копируем текст Compose file,
Итого
Создание в SoapUI асинхронного REST MockService с запуском в Portainer
Задача
Разработать мок для проверки асинхронного обмена сообщениями с внешней системой.
Как пример, рассмотрим некий кейс проверки валидности промокода внешней системой. По шагам:
1) Отправляем запрос в сервис внешней системы;
Запрос PUT содержит поля (например, в headers):
correlationId=рандомная строка, которую нужно будет вернуть в callback
replyTo= адрес сервиса принимающего callback
3) Через 5 секунд внешняя система отправляет асинхронный callback (по адресу replyTo из запроса):
Запрос POST с телом сообщения:
correlationId = correlationId из запроса
Решение
Для решения данной задачи используем SoapUI. Данный инструмент достаточно гибок в плане создания «умных» моксервисов, тк позволяет написать скрипты, как на этапах запуска/остановки моксервиса (Start Script/ Stop Script), так и перед/после получения запроса (OnRequest Script/AfterRequest Script).
Начнем:
1) В SoapUI создать новый проект, например, «test-soapui-project» (либо выбрать существующий) и создать для него два REST MockService.
в Path произвольный путь к моксервису, например, «/SP/ValidationSystem»,
в Port значение порта на котором будет запущен мок,
в Host значение «localhost».
Аналогично в Path произвольный путь к моксервису, например, «/SP/SpHost»
2) В мок Validation REST MockService добавить:
в Resource Path произвольный путь к методу внутри моксервиса, например, «/validation»
в Method указываем метод Rest запроса, в нашем случае PUT (шаг 1 в задаче)
для запроса PUT добавить ответ 204 No content
в Content | Media type тип ответа на запроса, в нашем случае «application/json».
переменные в Properties:
это переменные в которые будут сохранены поля promotionalCode, replyTo, correlationId из поступившего запроса PUT.
*log.info можно не использовать, носит информативный характер для проверки хода выполнения операций на вкладке script log.
3) В мок Validation Receiving CALLBACK REST MockService добавить:
в Resource Path произвольный путь к методу внутри моксервиса, например, «/callback»
в Method указываем метод Rest запроса, в нашем случае POST.
для запроса POST добавить ответ 200 OK
в Content | Media type тип ответа на запроса, в нашем случае «application/json».
4) Запустить оба моксервиса
5) Проверяем работу моксервисов
Отправляем запрос проверки валидации, например, через Postman:
Выполняем PUT вызов метода validation на Мок 1 (Validation REST MockService), указывая в Headers 3 параметра, как описано в Задаче
Запуск моксервиса в Portainer:
1) В images добавляем образ для работы с моксервисами SoapUI:
находим на docker hub необходимый образ. в нашем случае fbascheper/soapui-mockservice-runner (https://hub.docker.com/r/fbascheper/soapui-mockservice-runner/)
переходим в Portainer в раздел images
по названию загружаем необходимый образ
2) Разворачиваем стек для работы с моксервисами :
Составить Compose file:
*в нашем примере проект soapUI разместили на сервере в /soapui/soapui-prj/
перейти в Portainer в раздел Stacks,
добавить новый стек:
— указать в поле Name, как будет назван наш стек, например, sp-soapui-mocks,
— в Web editor копируем текст Compose file,
Итого
Mock Service SOAP UI
Mock Service реализует то, что также называют: Mock, Stub, Fake, Сервис-имитация, Заглушка
Сделаем сервис, который будет посылать нам сообщения в ответ на определённый тип запроса на определённый EndPoint
Для примера я выбрал сервис для сайта о велосипедах TopBicycle.ru заходите на этот сайт если любите велосипеды.
Аналогичный функционал можно реализовать с помощью любого веб-сервера.
File → Create Empty Project
Введите имя проекта
New Rest Mock Service
Укажите имя MockService
Add new mock action
Введите путь и выберите метод GET
Введите имя нового ответа
Скопируйте json и вставьте в тело ответа
Замените Content | Media type на application/json. Http Status Code должен быть 200 – OK. Headers введите по желанию. Их можно добавить нажав на зелёный плюс +
Правой кнопкой мыши кликните на BicycleService и
Add new mock action
Назовите его «bicycle/1»
Правой кнопкой мыши кликните на «bicycle/1» (или кликните Ctrl + N) и
create New MockResponse
назовите его «singleBicycle»
Скопируйте json и вставьте в тело ответа
Создайте новый mock action, имя оставьте старое «/bicycle» а тип измените на POST вместо GET.
Создайте новый MockResponse и назовите его «addBicycle»
Скопируйте json и вставьте в тело ответа
Создайте новый mock action типа GET и назовите его «/incorrectep» а новый MockResponse «400» Измените Http Status Code на 400 – Bad Request
Обратите внимание, что сервис BicycleService использует порт 8080
Запустите BicycleService нажав на зелёный треугольник
У нас есть четыре сценария
Используем Postman чтобы протестировать их
POST на http://localhost:8080/bicycle
Должен вернуть key 12041961
GET на http://localhost:8080/bicycle
Должен вернуть список велосипедов
GET на http://localhost:8080/bicycle/1
Должен вернуть первый велосипед
GET на http://localhost:8080/incorrectep
Должен вернуть пустой ответ и Http Status 400 Bad Request
Dynamic Response
Чтобы протестировать отправим несколько запросов из Postman и проверим изменяется ли ответ
Заглушки для веб-сервисов
Приложения 1С редко работают в изоляции — чаще всего они интегрируются с различными сервисами, которые могут быть расположены как в локальной сети предприятия, так и на удаленных серверах. И иногда встает вопрос тестирования этой интеграции в рамках общего сценарного тестирования. Посмотрим, с какими проблемами при этом можно столкнуться и при чем тут вообще эти самые «заглушки».
Содержание
С чем будем работать
Давайте предположим, что мы ведем разработку функционала, который обращается к удаленному веб-сервису, получает из него данные и запускает в базе 1С механизм расчетов. Не важно какие именно данные возвращает сервис: важно, то, что полученный результат прямо влияет на работу наших алгоритмов.
Предположим, что веб-сервис отвечает на запросы с ощутимым задержками, потому что обращается в нагруженную базу данных в реальном времени. При этом он расположен в VPN-сети и физическое его нахождение нам неизвестно: сервер может находиться в соседнем здании, может быть поднят в облаке или физически располагаться в зарубежном дата-центре.
В целом, используется стандартная схема: 1С обращается к сервису, который опубликован на веб-сервере; веб-сервис обращается к базе данных и возвращает результат.
Мы уже используем сценарное тестирование и хотим, чтобы наши тесты работали стабильно и не зависели от доступности удаленного сервера, загруженности серверного оборудования или от неполадок в сети. Поскольку прогон тестов — процесс не однократный, а регулярный (и в целом автоматический), мы хотим сделать так, чтобы в момент запуска тестов веб-сервис был всегда доступен и работал максимально быстро, стабильно и предсказуемо.
Обратите внимание, что мы не собираемся проверять работоспособность веб-сервиса и заниматься его тестированием — этим занимаются разработчики веб-сервиса. Нам нужно обеспечить его бесперебойную работу на момент проведения тестирования.
Получается, нам нужна такая «локальная копия» удаленного сервиса, которую можно настроить нужным нам образом, а своему приложению сказать: «Во время тестирования не ходи на удаленный сервер, а ходи на localhost».
Проблемы тестирования интеграции
Вообще, для использования заглушек веб-сервисов может быть много причин. Например, следующих:
Тесты замедляются. Если поставщик сервиса находится далеко, сетевая среда нестабильная и при вызове сервиса происходит задержка, то время прохождения тестов может значительно увеличиваться.
Тесты работают нестабильно. Веб-сервисы могут быть не всегда доступны из-за технических обновлений, подвержены перегрузкам или ошибкам сетевых протоколов.
Тесты покрывают не все возможные варианты ответов сервиса. Не всегда есть возможность получить некоторые ответы от реального веб-сервиса и промоделировать все рабочие ситуации — следовательно, максимально полно протестировать взаимодействие.
Доступ к рабочим веб-сервисам ограничен. Часто встречается ситуация, когда рабочие сервисы недоступны из тестового контура, в котором программисты ведут разработку. Это делает как по причине безопасности, так и для того, чтобы не подвергать лишней нагрузке рабочее окружение.
Из-за ошибок разработчика могут быть подвергнуты риску реальные данные. Например, есть веб-сервисы, которые позволяют добавлять или изменять данные в удаленной системе. Ошибка при вызове такого сервиса может привести к потере данных.
Давайте теперь поближе рассмотрим мок-объекты.
Заглушки
Существует несколько видов объектов, которые позволяют симулировать поведение реальных объектов во время тестирования:
Относительно веб-сервисов можно сказать, что мок-сервис позволяет переопределить реальный сервис и подставить вместо него упрощенную реализацию, которая работает нужным разработчику образом и дает доступ к собственным данным и настройкам. Мы как бы создаем у себя аналог удаленного веб-сервиса, который отвечает на вызовы нашего приложения.
Правда, тут возникают две сложности.
Во-первых, моки надо создать. Надо устанавливать дополнительный софт, описывать логику, загружать нужные данные в макет ответа. Не то чтобы это сложно, но это требует усилий и времени. Может быть, кому-то будет проще сохранить результат вызова в файл и при тестировании обращаться не к заглушке, а к файлу. В небольших проектах с малым количеством тестов такой подход вполне оправдан.
Во-вторых, веб-сервис может поменяться. Например, программист написал рабочую логику для заглушки, а через какое-то время веб-сервис стал работать по-другому (а разработчика, как часто бывает, об этом никто не уведомил). О том, что в работе веб-сервиса что-то поменялось он узнал, когда выкатил обновление на рабочую базу.
Решение простое — для сервиса, который может быть подвержен изменениям нужно сделать набор дымовых тестов (smoke-тесты). Дымовые тесты проверяют, что основные методы сервиса не изменились и обращение к ним не вызывает ошибок.
Мы рассмотрим способ создания мок-сервисов в программе SoapUI, которая является достаточно популярной в мире тестирования. Первоначальная схема работы примет такой вид:
Тестируемое приложение 1С больше не будет обращаться к удаленному сервису, а получит тот ответ, который мы заранее подготовили и опубликовали из SoapUI.
Запускаем моки через SoapUI
Шаг 0. Готовим окружение
Программа SoapUI (подробнее о ней можно прочитать в публикации Использование SoapUi для работы с веб-сервисами. Часть1) позволяет на основании WSDL-схемы сервиса создать Mock-service и опубликовать его на собственном веб-сервере, который умеет запускать.
SoapUI можно получить на официальном сайте компании SmartBear. Для наших целей достаточно бесплатной 32х-разрядной OpenSource версии — ее нужно скачать и установить.
В конфигурации 1С, которую мы дорабатываем, есть обработка Расчет трафика — она обращается к веб-сервису CalculateService и вызывает метод CalculateTraffic, в который передает несколько параметров, вроде ID, StoreID и других. Рассчитанный веб-сервисом результат записывается в регистр и запускает внутренний механизм расчетов (что он именно делает, в рамках статьи значения не имеет).
Мы дорабатываем механизм расчетов и во время проведения тестов хотим вместо ответов удаленного сервиса подставлять свои заранее заготовленные варианты.
! Поскольку я воспроизвожу пример для статьи на своем компьютере, в адресе вы видите «localhost». Но подразумевается, что в рабочей ситуации мы будем обращаться к рабочему сервису на рабочем сервере.
Для начала запустим браузер и убедимся, что веб-сервис доступен:
Дальше добавим веб-сервис в SoapUI и сделаем для него заглушку.
Шаг 1. Импортируем WSDL-схему
Запустим SoapUI и создадим новый проект.
Если при импорте возникает ошибка, дело скорее всего в авторизации: нужно ввести имя пользователя и пароль.
Забегая немного вперед, добавлю, что в SoapUI есть удобный способ сохранить данные авторизации: нужно заполнить параметры Username и Password на вкладке Auth, которая станет доступна в редакторе запроса к сервису:
В дереве проекта появился веб-сервис CalculateService.
Теперь мы можем заполнить параметры и обратиться к сервису:
Шаг 2. Запускаем mock-сервис
Задаем параметры MockService:
Обратите внимание на порт, на котором будет доступен сервис и на путь к нему.
Жмем ОК и заполняем имя мок-сервиса:
Открывается окно, из которого можно запустить мок-сервис и указать его настройки:
Зайдем в настройки и укажем, что мок будет подниматься на локальном интерфейсе localhost:
Жмем на Запуск. Если запуск пройдет успешно, станет доступна кнопка Открыть WSDL-схему в браузере:
В дереве проекта появился мок-сервис MoskCalculateService с методом CalculateTraffic:
Шаг 3. Настраиваем ответ
Открываем редактор ответа Response 1. В этой форме можно определить данные, которые будет возвращать наш мок-сервис.
Здесь можно заполнить значения полей ответа, подставить любую валидную XML или написать скрипт на Groovy, который будет определять содержание ответа в зависимости от запроса к сервису. Например, мы можем в получать в скрипте значения переданных параметров, считывать файлы с диска и возвращать их клиенту с результатом вызова, при это логгируя все входящие запросы.
Я покажу простой вариант: получаем из входящего запроса значения параметров StoreID и Offset, сам запрос сохраняем на диск, а клиенту возвращаем значение, рассчитанное по формуле Offset*24 + StoreID:
Я сохранил проект в d:\BASE1C\DEMO\MockCalculateService.xml
Запускаем проект SoapUI из консоли
Для того чтобы поднять мок-сервис из командной строки (а, следовательно, иметь возможность сделать это программным образом), нужно выполнить файл mockservicerunner.bat из директории C:\Program Files (x86)\SmartBear\SoapUI-5.5.0\bin.
Подробное описание параметров запуска mockservicerunner.bat можно найти на странице сайте SoapUI или в консоли:
Главный обязательный параметр — путь к xml-файлу с проектом из SoapUI. Программа считывает настройки проекта из файла и по умолчанию поднимает все мок-сервисы, которые в нем описаны.
Открываем консоль в папке c:\Program Files (x86)\SmartBear\SoapUI-5.5.0\bin\ и запускам наш сервис командной:
В консоли наблюдаем, что MockService started on port 8090:
Подробный лог запуска записывается в c:\Program Files (x86)\SmartBear\SoapUI-5.5.0\bin\soapui.log, а ошибки можно найти в файлах soapui-errors.log и errors.log
При запуске может возникнуть такая ошибка:
2019-03-04 11:30:05,556 ERROR [errorlog] java.lang.ClassNotFoundException: com.eviware.soapui.plugins.auto.factories.AutoImportMethodFactory
Проверим еще раз, что WSDL-схема доступна по адресу http://localhost:8090/MockCalculateService?WSDL.
Теперь мы умеем запускать заглушки с помощью командной строки. Пойдем дальше и попробуем прикрутить все это дело к Ванессе.
Подключаем Ванессу
Поскольку на Инфостарте уже есть замечательные статьи про тестирование и применение Ванессы на практике, сразу перехожу к делу.
План такой: при запуске теста будем поднимать наш мок-сервис, проверять его доступность, заполнять поля на форме и нажатие на кнопку Рассчитать выызвать его метод CalculateTraffic.
Определим примерный набросок сценария:
Поднимаем сервис из файла;
Проверяем, что сервис доступен;
Заполняем параметры на форме обработки;
Выполняем вызов процедуры, которая обращается к сервису;
Проверяем результат расчета;
Завершаем работу мок-сервиса.
Создадим файл ПроверкаСервиса.feature (я взял за основу одну из своих существующих фич) и загрузим его в Ванессу:
Дальше мы будем заниматься реализацией шагов. Итоговый текст фичи будет приведен в конце.
Подготовка данных
Пара слов о том, как будем хранить настройки для обращений к веб-сервисам.
Полный путь к файлу mockservicerunner.bat записываем в константу ПутьКЗапускателюЗаглушекВебСервисов, а для описаний веб-сервисов добавим справочник ВебСервисы:
При выполнении сценария каждый раз будем заполнять константу:
И добавлять новый элемент в справочник Веб-сервисы:
Делаем это потому, что другой тест или пользователь может перезаписать или удалить наши значения — то есть нет гарантии, что во время тесты в них будут заполнены правильном образом.
Элемент справочника будем загружать из макета. Так удобнее, чем генерировать заполнение полей через кнопко-нажималку.
В сгенерированной Ванессой обработке (этап генерации обработки я опускаю, подразумевая, что делать это мы умеем) нужно добавить макет и скопировать туда содержимое табличного документа:
Запускаем моки
Для запуска моков нам нужно выполнить батник mockservicerunner.bat с параметром d:\BASE1C\DEMO\MockCalculateService.xml.
Видим, что в стандартной библиотеке Ванессы есть шаг Я Запускаю команду с параметром, который позволяет выполнить команду систему с произвольным набором параметров.
Можно было бы воспользоваться этим шагом, но, как говорится — есть нюансы.
Во-первых, придется каждый раз писать что-то вроде «Я запускаю команду ‘c:\Program Files (x86)\SmartBear\SoapUI-5.5.0\bin\mockservicerunner.bat’ с параметром «d:\BASE1C\DEMO\MyServiceProject.xml» — это длинно и неудобно.
Во-вторых, библиотечный шаг выполняется только в синхронном режиме — то есть вызывающий процесс (в нашем случае это 1С) будет ожидать завершения работы вызываемой команды, чтобы продолжить свое выполнение. Это значит, что 1С запустит мок-сервис и будет висеть, пока он будет запущен. Не совсем то, что нам нужно.
Вот так реализован шаг Я запускаю команду в файле из поставки libraries\Плагины\step_definitions\Фича_УправлениеПриложениями.epf:
А вот реализация функции Выполнить команду ОС без показа черного окна в одной из главных обработок Ванессы bddRunner.epf:
Видно, что вызывается метод Run() объекта WScript.Shell и параметр ЖдатьОкончания вроде бы есть, но передать значения в него нельзя. Баг это или фича, честно говоря, я не понял 🙂 Удобнее всего для запуска моков создать свой шаг, в котором можно принудительно указать параметр метода Run().
Реализуем шаг Я запускаю моки из файла:
Ожидание запуска моков
Чтобы сделать цикличную проверку доступности сервисов, используем функцию ПодключитьОбработчикОжидания и функцию Ванессы ПродолжитьВыполнениеШагов:
Остановка мок-сервисов
После того как тестируемая программа выполнила обращение к мок-сервису, нужно остановить его работу.
При использовании графического интерфейса SoapUI все просто: для остановки мок-сервиса нужно на красную кнопку Stop the mock service.
При запуске через командную консоль тоже проблем нет — достаточно нажать сочетание клавиш Ctrl+C (или Ctrl+Z).
А в случае, когда мы запускаем процесс через WScript.Shell, все несколько сложнее — штатного способа остановить выполнение его нет.
Например, вот вопрос в официальном сообществе SoapUI, который так и остался без ответа: Stop mock service using mockServiceRunner.
Вопрос достаточно распространенный и разработчики предлагают разные решения:
Запоминать PID (идентификатор) процесса и «пристреливать» его после завершения теста. Пример для Linux: Sample script to start/stop soapui mockservicerunner with nohup
Сделать обертку над SoapUI, которая после запуска веб-сервиса будет пытаться прочитать внешний файл-флаг. Если файла нет — все ок, работаем дальше. Если файл появился — завершаем работу сервиса. Проект на гитхабе: Wrapper to start SoapUI MockService Runner
Написать собственное приложение или скрипт, который будет парсить файл проекта SoapUI, извлекать из него описание сервиса и поднимать собственный веб-сервис.
Пока в публичном доступе готового решения для Windows не найдено, остановимся на простом варианте — будем завершать работу процесса java.exe командой
это сработает, если больше нет запущенных процессов java.exe (точнее говоря, они все будут завершены в момент выполнения этой команды).
Или можно использовать более продвинутый вариант:
скрипт завершит работы всех процессов, у которых в параметрах запуска есть название нашего файла с проектом: «MyServiceProject.xml».
Коллеги, если у кого есть более изящное решение, сообщите 🙂
Результат
Теперь мы можем составить полный текст нашего сценария «Расчет трафика»: