Mock-сервисы для тестирования: How to use + Quick start

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

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

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

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

В данной статье будут приведены примеры с локальным развертыванием. Этот вариант идеально подойдет, если тестируемое приложение развернуто на рабочей машине. Если же стоит задача развернуть заглушку для удаленного сервиса, тут лучше обратиться к девопсам/администраторам. Т.к. политика безопасности не всегда дает доступ на сервер с крутящимся на нем приложением, а перед тем как будущая заглушка начнет отрабатывать – сервис придется перенастроить и перезагрузить. Иными словами сломать. Еще ни одного тестировщика за такие фокусы не похвалили. Потому в данной статье этот вопрос подниматься не будет.

Mock-сервисы для тестирования: How to use + Quick start

Немного теории

Существует несколько видов объектов, которые позволяют симулировать поведение реальных объектов во время тестирования:

  • Dummy — пустые объекты, которые передаются в вызываемые методы, но не используются. Предназначены лишь для заполнения параметров методов.
  • Fake — объекты, которые имеют реализации, но в таком виде, который делает их неподходящими для использования в рабочей ситуации.

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

  • Mock — объекты, которые заменяют реальный объект в условиях теста и позволяют проверять вызовы своих методов. Содержат заранее подготовленные описания вызовов, которые они ожидают получить. Применяются в основном для тестирования поведения пользователя.

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

Итак - приступим.

Mock-сервисы для тестирования: How to use + Quick start

Старый и всем давно известный инструмент. Пригоден как для тестирования SOUP, так и REST сервисов, автоматизации их проверок и создания заглушек.

Для тех, кто все еще путается:

REST и SOAP не сопоставимы!

- REST — это архитектурный стиль, оперирующий JSON через HTTP.

- SOAP — это формат обмена XML сообщениями с ограничениями по структуре сообщений через HTTP.

Рассмотрим на примере REST сервиса. Для SOAP шаги будут идентичными и не вызовут особых трудностей.

При первом запуске автоматически всплывает окно Endpoint Explorer, в котором необходимо указать сам запрос и необходимые для работы заголовки:

После пробного запроса получаем ошибку, что такой ресурс не существует. Сюда же можно отнести случай, когда сервис есть, но возвращает не совсем то, что нужно:
После пробного запроса получаем ошибку, что такой ресурс не существует. Сюда же можно отнести случай, когда сервис есть, но возвращает не совсем то, что нужно:
Mock-сервисы для тестирования: How to use + Quick start
Mock-сервисы для тестирования: How to use + Quick start

Откроем свежесозданную заглушку и скорректируем параметры запроса и ответа. Для этого нужно развернуть список RestMockService и провалиться в иерархии до Response:

Mock-сервисы для тестирования: How to use + Quick start

Затем двойным кликом открываем сам сервис и запускаем заглушку:

Mock-сервисы для тестирования: How to use + Quick start

Итак – наша заглушка активна. Теперь ради демонстрации клонируем ранее созданный запрос. В нем нам необходимо изменить хост. По умолчанию заглушка будет запущена на localhost:8080 с сохранением пути названия метода (resource):

Mock-сервисы для тестирования: How to use + Quick start

Как видно – после запуска стал возвращаться указанный ранее ответ и заголовок.

Теперь осталось только направить тестируемое приложение на наш локальный адрес. Для остановки заглушки необходимо повторно открыть RestMockService и нажать на кнопку Stop:

HKEY_LOCAL_MACHINE->SYSTEM->CurrentControlSet->Service->sTcpip->Parameters

Добавить новый ключ с именем “MaxUserPort” типа DWORD и десятичным значением 65534.

В более ранних версиях после получения ответа метода была возможность в один клик из контекстного меню поднять заглушку. По умолчанию запуск производился на localhost:8080. Спустя время эту фичу переработали. Теперь заглушка размещается непосредственно на серверах Postman. В бесплатной версии количество таких моков ограниченно 1000 в месяц. Это отлично подходит для демонстрации возможностей или мелкого тестирования, но подход весьма не практичен. Все же, лучшее – враг хорошего. На этом фоне древний SoapUI выигрывает по всем параметрам. Но описать процесс все же стоит.

Для этого нужно:

  1. Перейти в раздел Mock Servers
  2. Указать HOST + API Path, код ответа и тело ответа:
Mock-сервисы для тестирования: How to use + Quick start

3. Задать имя mock сервера, указать окружение и скопировать полученный URL:

Mock-сервисы для тестирования: How to use + Quick start

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

Mock-сервисы для тестирования: How to use + Quick start

Для корректной работы к получившейся ссылке необходимо добавить API Path:

Mock-сервисы для тестирования: How to use + Quick start

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

6. Также присутствует возможность заглушить метод из ранее сохраненной коллекции, но для этого придется дополнительно создать пример ответа внутри метода. Без этого mock работать не будет. Внутри необходимо указать все те же параметры, что описывались в пункте 2:

Mock-сервисы для тестирования: How to use + Quick start

Следующий инструмент просто подарок для всех айтишников.

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

Варианты установки

В данном варианте предоставляется выбор.

Во-первых, инструмент поддерживает Java/Python и может быть добавлен в проект автотестов через зависимости.

Во-вторых, может быть развернут в докере.

В-третьих, может быть установлен отдельным приложением. WireMock Studio — это инструмент, построенный на базе основного движка WireMock, с полным веб-интерфейсом, управлением несколькими фиктивными API и поддержкой OpenAPI и Postman.

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

$ java -jar wiremock-jre8-standalone-2.32.0.jar

Сервер по умолчанию запускается на порту 8080. В дальнейших примерах будем использовать переменную wireUrl = http://localhost:8080.

2. Создание заглушки

Способ 1

Отправляем POST запрос на url {{wireUrl}}/__admin/mappings/new с телом запроса:

{ "request": { "url": "/test/api", "method": "GET" }, "response": { "status": 200, "body": "Success!" } }

Теперь, если отправить GET запрос на {{wireUrl}}/test/api, то в ответе получим значение заглушки (Success!). Наша первая заглушка готова!

Настройки заглушек могут быть очень гибкими. В качестве url ожидаемого запроса можно указать не только конкретный url, но и urlPattern с помощью regexp.

Также можно задать паттерны в качестве ожидаемого тела запроса. Это могут быть как выражения xPath и jsonPath, так и точное соответствие:

{ "request": { "url": "/", "method": "POST", "bodyPatterns": [{//указываем ожидаемое тело запроса "matchesXPath": "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns58:Get_List", "xPathNamespaces": { "SOAP-ENV": "http://schemas.xmlsoap.org/soap/envelope/", "ns58": "http://test.envelope.com" } } ] }, "response": {...} }

Есть возможность задать ожидаемые:

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

Способ 2

После первого запуска в директории с джарником были созданы 2 папки: __files и mappings. Добавим в последнюю директорию json файл, в котором руками опишем все требуемые параметры. Для начала зададим общую структуру:

{ "mappings": [ ] }

Внутри квадратных скобок через запятые будем добавлять тестовые моки. Аналог предыдущего примера:

{ "request": { "method": "GET", "url": "/two" }, "response": { "status": 200, "body": "Updated" } }

Немного усовершенствуем наш мок. Добавим Json в ответе вместо строки, заголовок и куки:

{ "priority": 1, "request": { "method": "GET", "url": "/one" }, "response": { "status": 200, "jsonBody": { "arbitrary_json": [1, 2, 3] }, "statusMessage": "Everything was just fine!", "headers": { "Content-Type": "application/json;charset=UTF-8", "Set-Cookie": ["session_id=91837492837", "split_test_group=B"] } } }

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

"url": "/three?parameter=1"

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

{ "request": { "method": "GET", "urlPath":"/four", "queryParameters":{ "inn":{ "equalTo":"6663003127" // прямое соответствие }, "key":{ "matches":"(.*)" // любая строка }, "clientId":{ "matches":"(\\d{3})" //любые 3 числа } } }

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

{ "request": { "method": "ANY", "urlPattern": ".*" }, "response": { "status": 404, "jsonBody": {"status":"Error","message":"Endpoint not found"}, "headers": { "Content-Type": "application/json" } } }

3. Создание заглушки из файла

Вышеизложенные примеры довольно просты и не громоздки, но чаще всего необходимо, чтобы в ответе приходило нечто большее, чем просто “Успех”, а именно толстый json солидных размеров. В качестве ответа в этом случае удобно указывать конкретный файл.

Как уже оговаривалось выше – файлы читаются из директории __files, которая создается автоматически при первом запуске мок-сервера в папке, где лежит джарник. Следует помнить, что путь указывается относительным. Экранировать слеш не нужно. Стоит отметить, что в качестве bodyFileName можно передать не только json, но и xml, и просто текстовый файл.

{ "request": {...}, "response": { "status": 200, "bodyFileName": "/statistics.json" } }

4. Создание сценариев

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

Например, когда состояние объекта меняется в процессе выполнения теста. Для этого у WireMock есть сценарии:

{//нулевой шаг сценария "scenarioName": "Read_Item", // задаём название сценарию "requiredScenarioState": "Started", //указываем, на каком шаге сценария мы находимся. Первый шаг всегда называется Started. "request": {...}, "response": {...} } {//перый шаг сценария "scenarioName": "Read_Item", "requiredScenarioState": "Started", "newScenarioState": "Step2", //указываем название следующего шага сценария "request": {...}, "response": {...} } {//второй шаг сценария "scenarioName": "Read_Item", "requiredScenarioState": "Step2", "newScenarioState": "Step3", "request": {...}, "response": {...} }

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

Чтобы посмотреть список созданных сценариев и на каком шаге выполнения находится каждый из них, выполните GET запрос на {{wireurl}}/__admin/scenarios. Для сброса всех сценариев в дефолтное состояние необходимо вызвать {{wireurl}}/__admin/scenarios/reset

Подробнее описано в документации:

{ "scenarios": [ { "id": "dfc02", "name": "Init_refund", "state": "Step2",//сценарий пройден до конца "possibleStates": [ "Started", "Step2" ] }, { "id": "f234e", "name": "Read_Doc", "state": "Started",//сценарий ещё не начинался, или находится на нулевом шаге "possibleStates": [ "Started", "Step2", "Step3" ] } ] }

5. Recorder

Этому стоит уделить пару строк, но задерживаться особо не имеет смысла. Т.к. вещь может быть весьма полезна, но также и интуитивно понятна. Иными словами – user friendly. После запуска сервера WireMock необходимо открыть страницу с адресом:

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

docker run -it --rm \ -p 8080:8080 \ --name wiremock \ wiremock/wiremock:2.32.0

Образ Docker поддерживает точно такой же набор аргументов командной строки, как и отдельно скачанный джарник. Их можно передать в контейнер, добавив в конец команды:

--https-port 8443 --verbose

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

  • изменение контрагента при покупке товара
  • получение нотификаций
  • обновление биржевых котировок и т.п.

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

Из этого следует, что ситуации, когда приходится их глушить, тоже нередки. Допустим, фронтенд уже готов и задеплоен, а поставщика данных все еще нет. Если наш функционал – это простые уведомления, все не так страшно. А вот к примеру, когда на основе полученных данных, в режиме реального времени строятся графики и тут же на лету меняются, то ждать, когда бэкенд разродиться уже не позволительно. Решено – нужно ставить затычку!

WireMock позволяет работать с вебхуками как из Java, так и в автономном режиме. Рассмотрим последний вариант, т.к. использование внутри Java c Gradle/Maven зависимостями более чем подробно описано в руководстве.

1. Установка и запуск

Помимо основного Jar-файла необходимо скачать Jar-расширение для вебхуков. Хранить оба джарника желательно вместе.

Далее при запуске в командной строке указать наше расширение.

$java -cp wiremock-jre8-standalone-2.33.1.jar:wiremock-webhooks-extension-2.33.1.jar \

com.github.tomakehurst.wiremock.standalone.WireMockServerRunner \

--extensions org.wiremock.webhooks.Webhooks

2. Создание заглушки

По аналогии с ранее проведенными операциями, добавим внутрь json из директории mappings следующее:

{ "request": { "urlPath": "/something-async", "method": "POST" }, "response": { "status": 200 }, "postServeActions": [ { "name": "webhook", "parameters": { "method": "POST", "url": "http://my-target-host/callback", "headers": { "Content-Type": "application/json" }, "body": "{ \"result\": \"SUCCESS\" }" } } ] }

Здесь можно заметить, что основные ключи все те же, а вот самый интересный для нас – postServeActions. Именно внутри него определяется поведение нашей заглушки, отличной от простого REST-запроса.

Но вебхук не был бы вебхуком, если бы каждый раз отвечал молниеносно. Для более правдоподобной имитации необходимо прописать задержки. Для этого внутри postServeActions после url добавим следующий блок:

"delay": { "type": "fixed", "milliseconds": 1000 }

Таким образом можно обеспечить фиксированную задержку при ответе. Для случайной задержки тоже есть решение:

"delay": { "type": "uniform", "lower": 500, "upper": 1000 }

gRPC появился еще в 2015 году на основе протокола HTTP 2.0 и используется преимущественно в высоконагруженных системах. О преимуществах можно говорить долго, но это тема другой статьи. Наша же задача состоит в том, чтобы и их научиться “глушить”.

На русскоязычном пространстве технология не достаточно прижилась, и лично мне доводилось с ней сталкиваться не так часто. Тем не менее работать с ней все равно придется. В качестве клиента отлично подойдет Kreya. Также при работе с gRPC стоит учитывать, что коды ответов отличаются от стандартных. Ниже приведена таблица соответствий:

Mock-сервисы для тестирования: How to use + Quick start

1. Установка и запуск

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

Перво-наперво выкачиваем проект с гитхаба.

Для win также понадобится Docker Desktop.

Запуск контейнера:

docker run -p 8888:8888 -p 50000:50000 -v $(pwd)/example/proto:/proto -v $(pwd)/example/wiremock:/wiremock adven27/grpc-wiremock

2. Создание заглушки

Установка заглушки – все то же добавление в json:

{ "request": { "method": "POST", "url": "/BalanceService/getUserBalance", "headers": {"withAmount": {"matches": "\\d+\\.?\\d*"} }, "bodyPatterns" : [ { "equalToJson" : { "userId": "1", "currency": "EUR" } } ] }, "response": { "status": 200, "jsonBody": { "balance": { "amount": { "value": { "decimal" : "{{request.headers.withAmount}}" }, "value_present": true }, "currency": { "value": "EUR", "value_present": true } } } } }

Или через POST запрос, используя консоль или любой на вкус REST клиент:

curl -X POST http://localhost:8888/__admin/mappings \ -d '{ "request": { "method": "POST", "url": "/BalanceService/getUserBalance", "headers": {"withAmount": {"matches": "\\d+\\.?\\d*"} }, "bodyPatterns" : [ { "equalToJson" : { "userId": "1", "currency": "EUR" } } ] }, "response": { "status": 200, "jsonBody": { "balance": { "amount": { "value": { "decimal" : "{{request.headers.withAmount}}" }, "value_present": true }, "currency": { "value": "EUR", "value_present": true } } } } }'

3. Контрольная проверка:

grpcurl -H 'withAmount: 100.0' -plaintext -d '{"user_id": 1, "currency": "EUR"}' localhost:50000 api.wallet.BalanceService/getUserBalance

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

В случаях, когда разработчики уже реализовали сервис, proto файл, необходимый для работы уже существует. В данной ситуации логичнее его позаимствовать и избавить себя от рутины. Для этого немного изменим Dockerfile перед запуском:

FROM adven27/grpc-wiremock # copy our own proto files COPY ./proto/*.proto /proto/ # Start image faster by compiling everything when we build the image RUN gradle compileJava # copy wiremock mappings and files - could also be mounted COPY ./wiremock/mappings/* /wiremock/mappings/ COPY ./wiremock/__files/* /wiremock/__files/

Java application

Ввиду того, что оригинальное приложение может не иметь конечной реализации, предлагаю создать пример кода для тестирования. Это же в свою очередь можно расценивать как сервис заглушек.

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

Предварительная подготовка

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

Mock-сервисы для тестирования: How to use + Quick start

В моем случае это будет Maven Project, Spring Boot 2.7.0, Language Java. Metadata оставим без изменений, а вот в Dependencies добавим Lombok и Spring Web.

2. Генерируем и скачиваем проект.

3. Полученный архив распаковываем и открываем через Idea. Даем студии подтянуть все зависимости и настроить среду.

4. В получившейся иерархии создадим 3 пакета, которые будут соответствовать слоям: слой контроллеров, слой представления и слой сервисов:

Mock-сервисы для тестирования: How to use + Quick start

Создание контроллера

Контроллер – это и есть имя нашего метода. Иными словами, хвост, за который можно уцепиться. Для примера создадим класс FirstController. Благодаря аннотациям вся основная работа за нас уже была проделана. Стоит уделить внимание следующим:

RequestMapping – все запросы будут иметь в пути указанный префикс.

GetMapping – этим мы сообщаем, что наш запрос GET, который будет доступен по переданному имени, также здесь возможно передать часть пути к ресурсу.

Аналогично GET существуют POST, DELETE, PUT маппинги. Каждая аннотация указывает на принадлежность к определенному типу запроса.

package com.example.demo.controllers; import com.example.demo.dto.FirstMockResp; import com.example.demo.services.impl.MyService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor @RestController @RequestMapping("/test") public class FirstController { @GetMapping("/page2") public ResponseEntity<String> test() { return ResponseEntity.ok("success"); } }

После запуска данный пример вернет строку “success” и код ответа 200 ОК.

Усложним первый пример. Добавим к запросу передаваемые параметры через аннотацию RequestParam, причем один из них сделаем необязательным. Внутрь нашего класса добавим еще один контроллер:

@GetMapping("/hello") public String sayHello(@RequestParam(value = "paramOne") String paramOne, @RequestParam(value = "paramTwo", required = false) String paramTwo) { return String.join(" ", "Hello Worlds", paramOne, paramTwo == null ? "" : paramTwo).strip(); }

В данном примере вернется строка с конкатенацией переданных в нее значений. Стоит отметить, что если в вызове не передать paramOne, то наш метод свалится в ошибку. Для paramTwo такого не случится, т.к. этот параметр мы отметили как необязательный.

Описание структуры данных

Метод работает, но на полноценный API не похоже. Нужно возвращать структурированные данные, а не простые строки.

Для этого в пакете DTO создадим класс, описывающий структуру будущего ответа:

package com.example.demo.dto; import lombok.Builder; import lombok.Data; import java.util.List; import java.util.Map; @Data @Builder public class FirstMockResp { private String name; private String info; }

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

  1. Указать все параметры списком
private List locations=List.of("uuid","long","ampl");

2. Указать маппинг с указанием структуры

private Map<String,String> locations;

3. Создать экземпляр класса с отсылкой на вложенную структуру

private Location locations;

Остановимся на последнем варианте. Для этого в этом же пакете добавим класс Location:

package com.example.demo.dto; import lombok.Builder; import lombok.Data; import java.util.Map; @Data @Builder public class Location { private String key1; private String key2; private String key3; }

Таким образом, класс FirstMockResp примет вид:

package com.example.demo.dto; import lombok.Builder; import lombok.Data; @Data @Builder public class FirstMockResp { private String name; private String info; private Location locations; }

Сервисный слой или контентное наполнение

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

package com.example.demo.services.impl; import com.example.demo.dto.FirstMockResp; import com.example.demo.dto.Location; import org.springframework.stereotype.Service; @Service public class MyServiceImpl implements MyService { @Override public FirstMockResp getTestDto() { return FirstMockResp.builder() .name("some name") .info("another some information") .locations(Location.builder() .key1("qweqwe") .key2("qweqw") .key3("qwe") .build()) .build(); } }

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

package com.example.demo.services.impl; import com.example.demo.dto.FirstMockResp; public interface MyService { FirstMockResp getTestDto(); }

В конечном счете вернемся к нашему первому контроллеру. Он примет следующий вид:

private final MyService myserv; @GetMapping("/page2") public FirstMockResp test() { return myserv.getTestDto(); }

Запустим приложение для проверки и посмотрим, что вернет контроллер по адресу http://localhost:8080/test/page2

Mock-сервисы для тестирования: How to use + Quick start

Как видно из примера, полученная структура полностью соответствует описанию.

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

Также есть и способ решения задачи в лоб. Скажем, имея какой-то конечный json, можно отредактировать его value и заменить на переменные, используемые в Postman/Jmeter ${some_variable}. Тогда задача немного меняется. Нам больше не нужно описывать ключи и структуру ответа, т.к. она уже есть, остается только:

  1. Прочитать исходный файл как текст.

  2. Выполнить задачу по поиску подстроки (заранее определенные имена переменных) с заменой на наше усмотрение (генерация, подстановка из списка, чтение данных из файла и тп).

  3. Преобразовать текст в json.

  4. Передать полученный json в ответ контроллеру.

Вариантов реализации уйма. Выбирайте наиболее подходящий для вас, в зависимости от поставленной задачи.

Разумеется, WireMock можно спокойно подключить в Java проект для тестов. Использование в JUnit5 и JUnit4 немного отличаются. Конкретные различия указаны в документации:

Ниже будет приведен пример использования WireMock в тестах, с использованием наиболее популярного Rest Assured.

Зависимости

Maven

<dependency> <groupId>com.github.tomakehurst</groupId> <artifactId>wiremock-jre8</artifactId> <version>2.33.1</version> <scope>test</scope> </dependency>

Gradle

testImplementation "com.github.tomakehurst:wiremock-jre8:2.33.1"

Управление mock сервером

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

private WireMockServer wireMockServer; @Before public void setUp() { wireMockServer = new WireMockServer(options().port(8008)); wireMockServer.start(); } @Test public void test() throws JsonProcessingException { //some test } @After public void thearDown() { wireMock.stop(); }

При использовании JUnit можно обойтись аннотацией Rule. Это позволяет не заботиться о том, как и когда сервер будет остановлен:

@Rule public WireMockRule wm = new WireMockRule (options().port(8008));
@Rule public WireMock wm = new WireMockRule (options().extensions(new ResponseTemplateTransformer(true)).port(8008)); @Test public void test() throws JsonProcessingException { String body = mapper.writeValueAsString(new Data(new Reservation(“{{request.path/[1]}}”).setStatus(“RESERVED”))); //описание матчера, принимающего регулярные выражения stubFor(get(urlPathMatching(“/stock/[0-9]{6}”)).willReturn(okJson(body)))); Response response = RestAssured.given .when() .body (“{\n\t”id\”: \123456\”\n}”) .post(“http:localhost:9060/buy”); logger.info (“Response: “ +response.getBody().asString()); Assert.assertThat( response.statusCode(), is(200));

Если у вас Spring Boot приложение и вы собираетесь использовать WireMock в тестах, то тут наблюдается проблема при запуске. Решение описано тут.

Заключение

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

Было полезно? Будем рады обратной связи :)

88
Начать дискуссию