Skip to content
Back to skills

Wiremock Stubs

ASecurity

Заглушки внешних партнёров на тестовом стенде через WireMock или WireMock Studio: эндпоинты из кода сервиса, стабы по контракту, негативы, retry-сценарии, вебхуки, кириллица без порчи, переключение сервиса на мок с доказательством журналом и трейсом, откат. Используй всегда, когда просят «сделай мок партнёра», «создай стаб», «замокай сервис», «эмулируй недоступность партнёра», и при ошибке Request was not matched. English: WireMock, mock API, stub, service virtualization.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 16, 2026
ai-agentspythonbashspringtestinggitapi

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Pro scans all 10 files and shows the line behind each finding

Scanned October 16, 2026

npx -y skills add KonstantinVCH/QA-agent-skills --skill wiremock-stubs --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Wiremock Stubs?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Wiremock Stubs
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/konstantinvch-wiremock-stubs/badge)](https://www.skillsdirectory.com/skills/konstantinvch-wiremock-stubs)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: wiremock-stubs
description: "Заглушки внешних партнёров на тестовом стенде через WireMock или WireMock Studio: эндпоинты из кода сервиса, стабы по контракту, негативы, retry-сценарии, вебхуки, кириллица без порчи, переключение сервиса на мок с доказательством журналом и трейсом, откат. Используй всегда, когда просят «сделай мок партнёра», «создай стаб», «замокай сервис», «эмулируй недоступность партнёра», и при ошибке Request was not matched. English: WireMock, mock API, stub, service virtualization."
license: MIT
compatibility: "Python 3 (скрипты только на stdlib); доступ к admin-API WireMock или WireMock Studio. Опционально: Jaeger Query API, Bitbucket Server (архив репозитория), Confluence или другая документация интеграций."
metadata:
  author: KonstantinVCH
  version: "1.0.0"
  language: ru
---

# Mock-окружение внешнего партнёра на тестовом стенде

## Настройка под проект

Адреса, режим сервера, шаблон имён сервисов в Jaeger, соглашения команды и механика переключения
стенда собраны в [references/project-config.md](references/project-config.md). Заполни его перед
первой работой. Если у проекта есть база знаний, сначала прочитай в ней страницы о моках и о
нужной интеграции.

- **Python 3** — все четыре скрипта на stdlib, зависимостей нет.
- **WireMock** — `WIREMOCK_URL`. Поддерживаются два режима (`WIREMOCK_MODE`, по умолчанию
  определяется сам):
  - `studio` — WireMock Studio с реестром `GET /api/v1/mock-apis`: много mock API на одном хосте,
    у каждого свой порт; мок называется именем, id или портом;
  - `plain` — обычный WireMock: admin-API на `<хост>:<порт>/__admin`; мок называется портом на
    хосте `WIREMOCK_URL` или `-` (сам `WIREMOCK_URL`). Команд `apis`, `create`, `delete-api` нет.
- Для шага «переключить сервис» — доступ к репозиторию конфигов деплоя и системе деплоя (для
  Bamboo — скилл `bamboo-deploy`).
- **Документация интеграций** — желательна (MCP Atlassian, если подключён, иначе REST Confluence):
  там лежит контракт метода партнёра (примеры тел, коды, синхронный или асинхронный обмен). Без
  неё тела ответов придётся угадывать.

## Инструменты скилла

```bash
SK=<путь к каталогу этого скилла>
python "$SK/scripts/wm.py" --help              # мок и стабы
python "$SK/scripts/adapter_paths.py" --help   # что надо застабить
python "$SK/scripts/jaeger_urls.py" --help     # куда сервис реально ходит
python "$SK/scripts/post_json.py" --help       # послать запрос за внешнюю систему (входящие)
```

Детали по шагам вынесены отдельно, чтобы не занимать контекст без нужды:

| Файл | Когда читать |
|---|---|
| [references/stubs.md](references/stubs.md) | шаг 5: перечень эндпоинтов, спецификация, имена, матчинг, шаблоны, сценарии, вебхуки |
| [references/inbound.md](references/inbound.md) | шаг 1 показал входящую интеграцию: как сыграть внешнюю систему самому (колбэки, вебхуки, постбэки) |
| [references/switch.md](references/switch.md) | шаг 6: два маршрута переключения, OAuth-провайдер, возврат на реальный адрес |
| [references/traps.md](references/traps.md) | при странном поведении и перед первым самостоятельным прогоном; там же статус проверок |
| [references/project-config.md](references/project-config.md) | настройка под проект |

## Правила, которые дороже всего стоят при нарушении

- **Мок часто общий на все тестовые стенды.** Правка одного стаба меняет поведение у всех, кто
  тестирует эту интеграцию прямо сейчас. Поэтому перед правкой существующего — снимок и
  предупреждение команде.
- **Чужой стаб не чинить.** Разбирая мок, легко найти дефект в стабе, который делал не ты:
  опечатку в вебхуке, слишком широкий матчер, устаревший ответ. Это находка, а не задача.
  Правильно — доложить владельцу или в задачу; неправильно — «заодно поправить». Стаб может быть
  сломан намеренно под чей-то текущий прогон, а «исправление» уронит его молча и в другом месте.
  Трогай своими руками только то, что относится к твоей задаче.
- **Снимок делай до правки, а не после.** `wm.py backup`; накатить его обратно можно только через
  `wm.py restore` — `add` создаст дубли с теми же uuid.
- **Переименование или отключение стаба — фикстура.** Возвращай в том же сеансе: чужой прогон не
  должен упасть из-за твоей проверки.
- **Верь только факту.** Журнал WireMock (`wasMatched`) и `http.url` client-спана в Jaeger.
  «Деплой SUCCESS» и «файл с мок-адресом существует» не доказывают ничего.
- **Кириллицу в JSON шли скриптами скилла** (`wm.py` для стабов, `post_json.py` для запросов за
  партнёра). Прямой `curl -d` из Git Bash или консоли Windows пишет `U+FFFD` необратимо: стаб
  приходится пересоздавать, а запись сохраняется с битыми ФИО.
- **Создание и удаление mock API — с согласия пользователя.** Порты общие, у мока может быть
  чужой потребитель, а удаление необратимо.
- Только тестовые стенды и предпрод: на prod моки невозможны и недопустимы.

---

# 0. Preflight

```bash
python "$SK/scripts/wm.py" apis | tail -3        # studio
python "$SK/scripts/wm.py" stubs - | head -3     # plain
```

Молчит — открой `{WIREMOCK_URL}/__admin/mappings` (или `/api/v1/mock-apis` для Studio) в браузере:
если UI живой, а API нет, остановись и сообщи.

Узнай версию сервера. Ловушки ниже проверены на **WireMock Studio, сервер 2.33.1**: фичи 3.x
(`subEvents`, лог вебхуков, новые матчеры) там недоступны, предлагать их не надо. На 3.x часть
ограничений снята — сверяйся со своей версией.

---

# 1. Какой мок вообще нужен

Мокать не то место — самая дорогая ошибка, и делается она до первой команды. Определи направление
вызова:

| Что нужно | Куда идти |
|---|---|
| Твой сервис → партнёр: отправка заказа, запрос каталога, колбэк партнёру | **этот скилл** |
| Партнёр → твой сервис: партнёр сам вызывает твой API и получает 404 | маршрутизация входящего трафика (ingress, шлюз), WireMock не участвует |
| Экран входа / OAuth2-провайдер | отдельный OAuth2-мок — [switch.md](references/switch.md#oauthoidc-провайдер) |
| Внешняя система зовёт твой сервис (вебхук, постбэк, колбэк результата) | мока быть не может — играй партнёра сам: [inbound.md](references/inbound.md) |
| «Партнёр» с состоянием и сложной логикой | отдельный сервис-эмулятор, если он есть в проекте, а не WireMock |
| WireMock внутри unit-теста сервиса | не этот скилл, это к разработчику |

**Не каждую интеграцию можно замокать — решает направление, а не «известность» партнёра.**
Провайдер, к которому сервис ходит наружу, мокается. Провайдер, который сам постит данные тебе,
мока не имеет и не может иметь: интеграция **входящая**. Пример: браузер уходит на страницу оплаты,
провайдер постит результат в `payment-service`, а в карточке метода так и написано «Вызывающий
сервис: внешняя система». Бэкенд к провайдеру за этим наружу не ходит вовсе — исходящих вызовов
самого сервиса в трейсах ноль. Подменять нечего: WireMock работает только с исходящими.

Отсюда рабочая проверка перед тем, как идти дальше. Три факта, каждый быстрый:

```bash
python "$SK/scripts/wm.py" apis --grep <партнёр>           # есть ли мок вообще (studio)
python "$SK/scripts/jaeger_urls.py" <сервис> <стенд>       # ходит ли сервис наружу
```

плюс карточка метода в спецификации: поле «Вызывающий сервис». Если там внешняя система —
направление входящее, и задача решается **без** мока: партнёра играешь сам, посылая тот запрос,
который прислал бы он. Это не тупик и обычно быстрее заглушки — рецепт, скрипт и пример с
негативным сценарием в [references/inbound.md](references/inbound.md).

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

---

# 2. Найти существующий мок

В живом проекте моков обычно уже десятки — новый нужен редко. В Studio реестр отдаёт имя, id и
порт одним запросом, искать можно по любому из трёх:

```bash
python "$SK/scripts/wm.py" apis --grep payment   # 18063  448ew  RUNNING  payment-gateway-adapter
python "$SK/scripts/wm.py" stubs 18063           # или stubs payment-gateway-adapter
```

`stubs` печатает `uuid · приоритет · метод · код · url · имя · флаги`, где флаги — `tmpl`
(включён `response-template`), `webhook`, `scenario=…`, `delay=…ms`.

Второй источник — эталон в git: мок-профиль сервиса в репозитории конфигов деплоя. Порт оттуда —
гипотеза: файл может расходиться с конфигом стенда, и именно так однажды возник дефект. Факт даёт
трейс.

---

# 3. Создать mock API, если нужного нет

🚦 Покажи пользователю, что создаётся и почему не подходит существующий, дождись «да».

```bash
python "$SK/scripts/wm.py" create "<имя-сервиса-потребителя>"     # studio
```

Имя — как репозиторий сервиса-потребителя: тогда порт находится по имени сервиса без реестра. В
Studio порт назначается автоматически, шаблон `blank`, мок сразу `RUNNING` и слушает
`{WIREMOCK_URL}:<порт>` без деплоя. В обычном WireMock новый мок — отдельный процесс или контейнер,
поднимается средствами инфраструктуры.

Если в команде есть регламент — задача на добавление мока по шаблону из `project-config.md`.

---

# 4. Снимок ДО

```bash
python "$SK/scripts/wm.py" backup 18063 -o payment-18063-BEFORE.json
```

Файл в utf-8, положи к рабочим файлам задачи (и в базу знаний, если она есть). Откат потом:

```bash
python "$SK/scripts/wm.py" restore 18063 payment-18063-BEFORE.json         # покажет, что снесёт
python "$SK/scripts/wm.py" restore 18063 payment-18063-BEFORE.json --yes
```

---

# 5. Собрать перечень эндпоинтов и написать стабы

**Стаб нельзя написать, не зная контракта.** Ни код, ни документация по отдельности его не дают —
нужны оба, и каждый отвечает на свои вопросы:

| Источник | Что даёт только он |
|---|---|
| **Код сервиса** (блок клиентов в `application.yml` или `.path("…")` в коде) | какие пути сервис реально зовёт, каким **методом**, с какими `timeouts` и `retry.max-attempts` |
| **Спецификация** (страница на метод) | **тела** запроса и ответа примерами, коды ответов, **синхронный или асинхронный** обмен, JSON или **XML**, аутентификация |
| **Действующий мок и его журнал** | что уже застаблено, что уже стреляло 404, принятый стиль имён |

Пропустишь код — не будешь знать, что стабить, и половина путей отдаст 404 посреди флоу.
Пропустишь спеку — стабы получатся с выдуманными телами: `{"status":"ok"}` вместо контракта, и
проверка выродится в «запрос дошёл». Асинхронный обмен без спеки вообще не увидишь: сделаешь стаб с
телом там, где партнёр отвечает колбэком.

Перечень путей — скриптом:

```bash
python "$SK/scripts/adapter_paths.py" <источник> -o paths.txt
python "$SK/scripts/wm.py" cover 18063 paths.txt        # exit 2 = есть дырки
```

`<источник>` — каталог склонированного репо, `.zip` архива ветки (Bitbucket/GitHub → Download) или
slug репозитория с `--pat` (Bitbucket Server). Репо часто не склонирован, и это не повод
пропустить шаг.

`adapter_paths.py` достаёт пути **вместе с методами** (в конфиге методов нет, они в коде) и попутно
печатает `timeouts.response` и `retry.max-attempts` — оба нужны, чтобы придумать стаб с задержкой и
сценарий retry. Скрипт понимает Spring-конфиг вида `<префикс>.clients.<партнёр>.path` и литералы
`.path("/…")`; другая схема — перечень вручную в том же формате `МЕТОД /путь`.

Где искать контракт, что именно брать со страницы метода и почему пример важнее таблицы
параметров — [references/stubs.md](references/stubs.md#спецификация-метода).

Как писать сами стабы — [references/stubs.md](references/stubs.md): имена по соглашению команды
(например, `N. Бизнес-описание (вариант)`), матчинг через `urlPath` + `queryParameters`, разные
контексты шаблонов, обязательный негатив к каждому пути, сценарные стабы, вебхуки, опт-ин
заголовок вместо переименования.

Залить и проверить, что не побилось:

```bash
python "$SK/scripts/wm.py" add 18063 new-stubs.json    # объект, массив или {"mappings":[…]}
python "$SK/scripts/wm.py" stubs 18063
```

Правка существующего — целиком, иначе `PUT` потеряет поля, которых нет в теле:

```bash
python "$SK/scripts/wm.py" get 18063 <uuid> > stub.json
python "$SK/scripts/wm.py" update 18063 <uuid> stub.json
```

---

# 6. Переключить сервис на мок

Файл с мок-адресом сам по себе ничего не включает: по умолчанию сервис едет на реальный dev-адрес
партнёра. Два маршрута, условия включения и случай OAuth-провайдера —
[references/switch.md](references/switch.md).

Коротко: маршрут **A** (по умолчанию) — точечно переопределить URL партнёра в конфиге стенда в
ветке задачи и выкатить обычным деплоем, браузер не нужен. Маршрут **B** — включить мок-профиль
сервиса флагом деплоя целиком; в разобранном примере для этого нужна была браузерная сессия
системы деплоя.

---

# 7. Верификация

Без неё работа не закончена: два независимых источника отвечают на два разных вопроса — «дошло ли
до мока» и «тот ли адрес применился».

```bash
python "$SK/scripts/wm.py" log 18063 -n 20             # дошло ли и в какой стаб попало
python "$SK/scripts/wm.py" log 18063 --unmatched       # что не сматчилось
python "$SK/scripts/wm.py" log 18063 -n 1 --full       # запись целиком: заголовки и тело ответа
python "$SK/scripts/jaeger_urls.py" <сервис> <стенд>   # куда сервис реально ходит
```

| Признак в журнале | Значение |
|---|---|
| запись есть, `matched=True`, ожидаемое имя стаба | сервис дошёл до мока и попал куда надо |
| запись есть, `matched=False` | стаб не совпал: путь, метод, query или заголовок. WireMock отвечает `404 Request was not matched` с диаграммой сравнения — это не недоступность партнёра |
| записей нет вовсе | сервис **не переключён**, идёт на реальный адрес |
| `matched=True`, но в теле `?param=` вместо значения | сломанный шаблон, см. stubs.md |

`jaeger_urls.py` считает вердикт только по внешним вызовам: внутренние сервисы платформы под мок не
идут никогда и в знаменателе только мешают. Actuator у сервисов часто закрыт (`/actuator/**` → 404),
прочитать применённый конфиг через него нельзя.

Заголовки из журнала снимай целиком: фильтрация «интересных» однажды скрыла `X-Idempotency-Key`,
который отвечал на открытый вопрос партнёра про дедупликацию.

---

# 8. Откат фикстур и возврат стенда

Сразу после прогона, в том же сеансе — иначе фикстура достанется коллеге как «баг»:

```bash
python "$SK/scripts/wm.py" rename 18063 <uuid> "3. Подтверждение платежа"
python "$SK/scripts/wm.py" rm 18063 <uuid>
python "$SK/scripts/wm.py" reset-scenarios 18063
python "$SK/scripts/wm.py" stubs 18063 > AFTER.txt        # сверить с BEFORE
```

Состав не сходится — `restore <снимок> --yes`. Сервис вернуть: маршрут A — передеплой с основной
ветки конфигов, маршрут B — деплой со значением мок-флага по умолчанию.

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

---

# 9. Версионировать

- **Эталон стабов** → репозиторий, указанный в `project-config.md`, выгрузка
  `wm.py get <мок> > имя.json`. В эталон пиши корректные значения, а не слепок стенда с чужими
  временными отключениями.
- **Конфиг переключения** → PR в репозиторий конфигов деплоя.
- **Задача** → мок, порт, перечень стабов, какой сервис переключён и как вернуть.

---

# 10. Пример: негативный ответ платёжного шлюза

```
1. Направление: payment-gateway-adapter → шлюз. Порт из мок-профиля → 18063.
2. wm.py apis --grep payment     → 18063 448ew payment-gateway-adapter
   wm.py stubs 18063             → 8 стабов, 3 штатных + сценарий retry
3. wm.py backup 18063 -o payment-18063-BEFORE.json
4. adapter_paths.py <репо> -o paths.txt ; wm.py cover 18063 paths.txt   → дырок нет
5. Нужен «шлюз упал на первой попытке, успех на второй»: два стаба,
   scenarioName=payment-confirm-retry, priority 1, 503 → second-attempt → 200.
   wm.py add 18063 retry.json ; wm.py reset-scenarios 18063
6. Прогон → wm.py log 18063 -n 5
   POST /payments/confirm -> 503 matched=True stub="4. … (шлюз недоступен)"
   POST /payments/confirm -> 200 matched=True stub="5. … (успех после повтора)"
   jaeger_urls.py payment-gateway-adapter 3 → все внешние вызовы в мок
7. Откат: reset-scenarios, удалить временные стабы, сверить с BEFORE.
8. Экспорт эталона в репозиторий стабов, описать в задаче.
```

---

# 11. Шпаргалка

```
Studio UI      {WIREMOCK_URL}/mock-apis/<id>/stubs                  (SPA, только глазами)
Реестр моков   GET  /api/v1/mock-apis                               (Studio: имя+id+порт)
Admin по id    /api/v1/mock-apis/<id>/__admin/...                   (Studio: весь admin-API WireMock)
Admin по порту {WIREMOCK_URL}:<порт>/__admin/mappings               (любой WireMock)

wm.py            apis [--grep X] [--count]   stubs <мок>        get <мок> [uuid]
                 cover <мок> paths.txt [--prefix /p]             (exit 2 = есть дырки)
                 backup <мок> [-o f]         restore <мок> f.json --yes
                 add <мок> f.json            update <мок> uuid f.json
                 rename <мок> uuid "имя"     rm <мок> uuid
                 log <мок> [-n N] [--unmatched] [--full]         reset-log <мок>
                 scenarios <мок>             reset-scenarios <мок>
                 create "имя" [--force]      delete-api <мок> --yes      (только studio)
adapter_paths.py <каталог | .zip | slug --pat> [--client X] [--clients-key K] [--branch B] [-o f]
jaeger_urls.py   <сервис> [<стенд>] [--lookback 7d] [--trace ID]
post_json.py     <url> body.json [--set путь=знач] [--set-json путь=JSON] [--get] [--basic u:p] [--dry-run]
<мок> = studio: имя | id | порт;  plain: порт | -

Контракт  код: блок клиентов в application.yml (пути, таймауты, retry) + .post/.get в коде (методы)
          спека: страница метода (тела, коды, синхронный/асинхронный обмен, XML)
Проверка  wm.py log (wasMatched) + jaeger_urls.py; Matched-Stub-Id, не -Name
Смежное   bamboo-deploy (релизы и деплой), opensearch-logs (логи пода),
          qa-task-testing (прогон проверок по задаче)
```

Files in this skill

  • SKILL.md26.3 KB
  • references/inbound.md9 KB
  • references/project-config.md5.5 KB
  • references/stubs.md18.7 KB
  • references/switch.md9 KB
  • references/traps.md18.2 KB
  • scripts/adapter_paths.py16.8 KB
  • scripts/jaeger_urls.py9 KB
  • scripts/post_json.py7.5 KB
  • scripts/wm.py22.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.