Задача
Разработать мок для проверки асинхронного обмена сообщениями с внешней системой.
Как пример, рассмотрим некий кейс проверки валидности промокода внешней системой. По шагам:
1) Отправляем запрос в сервис внешней системы;
Запрос PUT содержит поля (например, в headers):
-
promotionalCode=correctPromotionalCode
-
correlationId=рандомная строка, которую нужно будет вернуть в callback
-
replyTo= адрес сервиса принимающего callback
2) Внешняя система отвечает синхронно — 204 No content;
3) Через 5 секунд внешняя система отправляет асинхронный callback (по адресу replyTo из запроса):
Запрос POST с телом сообщения:
-
status = «success», если promotionalCode запроса равен «correctPromotionalCode», в остальных случаях — «validationError»
-
correlationId = correlationId из запроса
Решение
Для решения данной задачи используем SoapUI. Данный инструмент достаточно гибок в плане создания «умных» моксервисов, тк позволяет написать скрипты, как на этапах запуска/остановки моксервиса (Start Script/ Stop Script), так и перед/после получения запроса (OnRequest Script/AfterRequest Script).
Начнем:
1) В SoapUI создать новый проект, например, «test-soapui-project» (либо выбрать существующий) и создать для него два REST MockService.
Первый назовем Validation REST MockService — имитирует сервис внешней системы:
-
в Path произвольный путь к моксервису, например, «/SP/ValidationSystem»,
-
в Port значение порта на котором будет запущен мок,
-
в Host значение «localhost».
Второй назовем Validation Receiving CALLBACK REST MockService — имитирует сервис принимающий callback (данный мок нужен для локальной проверки «Validation REST MockService» перед тем как мы его запустим на общем сервере):
Аналогично в Path произвольный путь к моксервису, например, «/SP/SpHost»
2) В мок Validation REST MockService добавить:
-
запрос PUT
Указываем:
-
в Resource Path произвольный путь к методу внутри моксервиса, например, «/validation»
-
в Method указываем метод Rest запроса, в нашем случае PUT (шаг 1 в задаче)
-
для запроса PUT добавить ответ 204 No content
Указываем:
-
в Http Status Code код ответа на запрос, в нашем случае «204 — No Content»,
-
в Content | Media type тип ответа на запроса, в нашем случае «application/json».
-
переменные в Properties:
это переменные в которые будут сохранены поля promotionalCode, replyTo, correlationId из поступившего запроса PUT.
-
OnRequest Script — данный скрипт будет выполняться перед отправкой синхронного ответа 204 No content (шаг 2 задачи). Получаем из запроса необходимые значения и записываем в Properties:
*log.info можно не использовать, носит информативный характер для проверки хода выполнения операций на вкладке script log.
-
AfterRequest Script — данный скрипт выполнится после отправки синхронного ответа. Тут наша задача сформировать callback на основании полученных данных, имитировать небольшую задержку (в нашем случае 5 секунд), отправить callback по адресу replyTo (шаг 3 задачи):
Сам скрипт:
log.info" Afterequest Script Синхрона - начало" import java.lang.Exception //Создаем переменные для формирования callback'а , используя значения из Properties def replyTo = context.mockService.getPropertyValue('replyTo') def correlationId = context.mockService.getPropertyValue('correlationId') def promotionalCode = context.mockService.getPropertyValue('promotionalCode') def status = "testStatus" if ( promotionalCode.contains("correctPromotionalCode")){ status="success" } else { status="validationError" } //Произвольное ожидание Thread.sleep(5000) log.info"до Отправки callback" // формируем callback - Запрос POST def conn = new URL(replyTo).openConnection(); def message = """\ { "status":"${status}", "correlationId":"${correlationId}" } """ conn.setRequestMethod("POST") conn.setDoOutput(true) conn.setRequestProperty("Content-Type", "application/json") conn.getOutputStream().write(message.getBytes("UTF-8")); def postRC = conn.getResponseCode(); println(postRC); if(postRC.equals(200)) { println(conn.getInputStream().getText()); } log.info"после Отправки callback" log.info" Afterequest Script Синхрона - конец"
3) В мок Validation Receiving CALLBACK REST MockService добавить:
-
запрос POST
Указываем:
-
в Resource Path произвольный путь к методу внутри моксервиса, например, «/callback»
-
в Method указываем метод Rest запроса, в нашем случае POST.
-
для запроса POST добавить ответ 200 OK
Указываем:
-
в Http Status Code код ответа на запрос, в нашем случае «200 — OK»,
-
в Content | Media type тип ответа на запроса, в нашем случае «application/json».
-
OnRequest Script — в скрипте мы выводим сообщение, что callback успешно получен и смотрим, что в нем пришло:
4) Запустить оба моксервиса
5) Проверяем работу моксервисов
Отправляем запрос проверки валидации, например, через Postman:
-
Выполняем PUT вызов метода validation на Мок 1 (Validation REST MockService), указывая в Headers 3 параметра, как описано в Задаче
-
В replyTo указываем адрес сервиса принимающего callback — Мок 2 (Validation Receiving CALLBACK REST MockService)
При выполнении запроса видим в ответе — 204 No Content.
Проверяем в script log SoapUI , что callback пришел на Мок 2:
Запуск моксервиса в Portainer:
1) В images добавляем образ для работы с моксервисами SoapUI:
-
находим на docker hub необходимый образ. в нашем случае fbascheper/soapui-mockservice-runner (https://hub.docker.com/r/fbascheper/soapui-mockservice-runner/)<o:p>
-
переходим в Portainer в раздел images
-
по названию загружаем необходимый образ
2) Разворачиваем стек для работы с моксервисами :
-
Составить Compose file:
version: "3.7" services: soapui-validation: image: fbascheper/soapui-mockservice-runner:latest volumes: - /soapui/soapui-prj:/home/soapui/soapui-prj environment: - MOCK_SERVICE_NAME=Validation REST MockService - PROJECT=/home/soapui/soapui-prj/test-soapui-project.xml ports: - 7703:8080
где:
image — образ загруженный с docker hub (https://hub.docker.com/r/fbascheper/soapui-mockservice-runner/)
volumes — указать где на сервере расположен проект soapUI и куда его необходимо расположить в контейнере, формат: «директория на сервере»: «директория в контейнере»
MOCK_SERVICE_NAME — название моксервиса внутри проекта soapUI
PROJECT — путь к проекту soapUI в контейнере, формат: «значение «директория в контейнере» из volumes» + само название проекта(в нашем случае test-soapui-project)
ports — указать внешний порт по которому моксервис будет доступен извне и на каком порте запущен в контейнере (значение port из soapUI), формат «внешний порт»: «порт в контейнере»
*в нашем примере проект soapUI разместили на сервере в /soapui/soapui-prj/
-
перейти в Portainer в раздел Stacks,
-
добавить новый стек:
— указать в поле Name, как будет назван наш стек, например, sp-soapui-mocks,
— в Web editor копируем текст Compose file,
-
Deploy the stack.
В результате будет сформирован стек sp-soapui-mocks, в котором запущен сервис , указанный в compose file:
Итого
Мы получили асинхронный моксервис , который удовлетворяет поставленной задаче. Сервис запущен на общем сервере и доступен всем участникам проекта. В данный момент он помогает нам разрабатывать и тестировать приложение при отсутствии API сторонней системы.
ссылка на оригинал статьи https://habr.com/ru/post/549066/
Добавить комментарий