Docs WireMock Migration Guide

Migrating from WireMock to Mockarty

If you already maintain WireMock stubs (@wiremock/wiremock, the
standalone JAR, or the Docker image), you can keep them working unchanged
on Mockarty. This guide covers the migration path step by step.

What changes

  1. Server URL — point your existing WireMock clients (the admin API,
    /__admin/*) at your Mockarty host instead of the WireMock host. The
    system under test calls the stubs under the namespace’s stub base URL,
    http://<host>:5770/stubs/<namespace> — use that where you used the
    WireMock base URL. The request journal reports paths relative to that
    base, as WireMock does, so verify(getRequestedFor(urlEqualTo("/orders")))
    keeps working.
  2. Authentication — add the Mockarty API token (X-API-Key header or
    the SDK’s standard mechanism) so requests reach the admin server.

What stays the same

  • Your stub JSON files — the WireMock 3.x JSON schema is accepted as-is.
  • The /__admin/* admin REST endpoints — Mockarty exposes them on the
    same path.
  • The stub matching semantics — every officially documented matcher kind
    is supported (see the table below).

Drop-in docker replacement (--wiremock-only)

Start mockarty with the --wiremock-only flag and the resulting
container behaves like a standalone WireMock server — /__admin/*
runs without auth, stubs are served at the root of the container’s URL,
and WireMock compatibility is enabled at startup for the default
namespace (sandbox), so your testcontainers client gets a working mock
server on first boot.

docker run --rm -p 8080:8080 mockarty/mockarty-cli:latest --wiremock-only

Or as a Java test fixture:

GenericContainer<?> mockarty = new GenericContainer<>("mockarty/mockarty-cli:latest")
    .withCommand("--wiremock-only")
    .withExposedPorts(8080)
    .waitingFor(Wait.forHttp("/__admin/health"));
mockarty.start();
String adminUrl = "http://" + mockarty.getHost() + ":" + mockarty.getMappedPort(8080) + "/__admin";

What --wiremock-only changes:

  • /__admin/* no longer requires a Mockarty auth header (real
    WireMock has no auth either).
  • The default namespace (sandbox) auto-flips its wiremock-compat
    setting to true so you don’t need to PATCH the toggle after every
    restart.
  • Stubs answer at the root (http://<host>:8080/orders), exactly where the
    system under test would call a WireMock container. /api/* and
    /__admin/* keep their own meaning.
  • All other admin features (UI, TCM, security scanner, A2A) still
    load — the flag is additive, not subtractive. Migration paths
    outside this doc keep working.

The flag is intended for ephemeral CI containers / testcontainers
tests. For long-running multi-tenant Mockarty deployments use the
per-namespace toggle below.

Enabling WireMock compatibility per namespace

The feature is off by default and enabled per namespace so you can
roll it out one team or pipeline at a time.

Turn it on for a namespace over the API:

curl -X PATCH "$MOCKARTY/api/v1/namespaces/$NS/settings/wiremock-compat" \
  -H "Authorization: Bearer $MOCKARTY_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"wiremock_compat_enabled": true}'

GET the same path to read the current state. Send
{"wiremock_compat_enabled": false} to turn it back off.

WireMock compatibility ships with the Mock module — it is the same stubs
your mocks already are, driven through WireMock’s client. With the toggle on
but the Mock module absent from your licence, /__admin/* answers
402 {"error":"WireMock compatibility is not included in your license", "feature":"mock"} — the toggle alone is not enough.

Once both are in place, the following endpoints become reachable for that
namespace:

Method Path Description
GET /__admin/health Health check
GET /__admin/version Version handshake
POST /__admin/reset Reset everything: stubs, journal and scenario state
POST /__admin/settings Accepted and answered 200; Mockarty has no process-global stub settings, so the response lists the keys it ignored
GET / POST / DELETE /__admin/mappings List / create / delete all stubs (DELETE removes persistent stubs too)
GET / PUT / DELETE /__admin/mappings/{id} Read / update / delete a stub
POST /__admin/mappings/import Bulk import (with OVERWRITE / IGNORE duplicate policy)
POST /__admin/mappings/reset Remove all WireMock-imported stubs (persistent stubs survive)
POST /__admin/mappings/save No-op (Mockarty persists by default)
GET / DELETE /__admin/mappings/unmatched Stubs no journalled request ever matched / delete exactly those
POST /__admin/mappings/find-by-metadata Filter stubs by metadata
POST /__admin/mappings/remove-by-metadata Bulk-delete stubs by metadata
GET / DELETE /__admin/requests Request journal
POST /__admin/requests/reset Clear the journal
POST /__admin/requests/find Filter journal entries by method, all four url* forms, named headers matchers and bodyPatterns
POST /__admin/requests/count Count journal entries matching the same filters (behind verify(n, …))
POST /__admin/requests/remove Delete journal entries matching the same filters
GET / DELETE /__admin/requests/{id} Read / delete one journal entry
GET /__admin/requests/unmatched List unmatched requests
GET /__admin/requests/unmatched/near-misses For each unmatched request, the stubs that came closest
POST /__admin/near-misses/request The stubs closest to a request you describe
POST /__admin/near-misses/request-pattern The logged requests closest to a request pattern
POST /__admin/requests/remove-by-metadata Delete the journal entries served by stubs with the given metadata
GET /__admin/scenarios List scenarios + current state
POST /__admin/scenarios/reset Return all scenarios to Started
PUT /__admin/scenarios/{name}/state Force a scenario state
POST /__admin/recordings/start Start journal-based recording
POST /__admin/recordings/stop Stop and snapshot recorded entries into stubs
GET /__admin/recordings/status NeverStarted / Recording / Stopped
POST /__admin/recordings/snapshot Snapshot the journal without stopping
ANY /__admin/recordings/proxy/* Forward a request to the recording target and record the exchange
GET /__admin/files List body files
GET / PUT / DELETE /__admin/files/{name} Body file CRUD

Supported matchers

Every documented WireMock matcher kind is supported:

Matcher Notes
equalTo Case-sensitive; combine with caseInsensitive: true for case-insensitive
equalToIgnoreCase Native case-insensitive equality
contains / doesNotContain Substring containment
matches / doesNotMatch Java-style regex
equalToJson Structural equality; supports ignoreArrayOrder and ignoreExtraElements modifiers
matchesJsonPath JSONPath expression must produce a non-empty result
equalToXml XML equality of the element tree (whitespace between elements, comments and attribute order ignored); XMLUnit options below
matchesXPath XPath subset: paths, attribute filters, indexed selectors, text() predicates
binaryEqualTo Byte equality vs a base64 expected blob
before / after / equalToDateTime The expected date is RFC 3339, "now" or "now +3 days"; the actual value is read as RFC 3339 / ISO 8601, or in actualFormat (a Java pattern such as dd/MM/yyyy, or epoch / unix). truncateExpected / truncateActual, expectedOffset / expectedOffsetUnit (seconds to years) and applyTruncationLast are honoured
hasExactly Multi-value field equals exactly the listed predicates
includes Multi-value field includes (at least) every listed predicate
absent: true Field must be absent
matchesJsonSchema Body must validate against an inline JSON Schema (draft 2020-12 by default, draft-07 with schemaVersion). A body that is not JSON simply does not match; a schema that cannot be compiled matches nothing and reports the authoring error
and / or / not Logical combinators — every matcher above nests inside them, including matchesJsonSchema

URL matching honours all five WireMock forms: url, urlPath,
urlPattern, urlPathPattern and urlPathTemplate (RFC 6570 {var}
placeholders become named path parameters). url is the whole URL: with a
query string (/orders?status=open) the request must carry exactly that
query — another value, an extra parameter or none does not match.

Beyond the URL and the matcher table, the request pattern also supports
method, headers, queryParameters, cookies, bodyPatterns,
basicAuthCredentials (translated to an exact Authorization: Basic …
check), formParameters (matched against an
application/x-www-form-urlencoded body), multipartPatterns (each part
of a multipart/form-data body checked by name, headers and
bodyPatterns, matchingType ANY or ALL), pathParameters (the
variables of a urlPathTemplate, each checked by its matcher) and
clientIp (the caller’s address as Mockarty sees it — behind a proxy,
the forwarded client address).

A near-miss distance runs from 0 (would have matched) to 1 (nothing in
common): the method, the URL (weighted double, closer when more path
segments agree) and each header, query parameter and body condition of
the stub count. Up to three near misses are returned, nearest first.

Matcher modifiers. Every WireMock modifier is applied:
caseInsensitive; the date options above; matchingType (ANY / ALL) on
multipart patterns; schemaVersion on matchesJsonSchema (V202012 — the
default — and V201909 validate as draft 2020-12, V7 / V6 / V4 as draft-07;
a $schema inside the schema wins); xPathNamespaces on matchesXPath;
the object form of matchesJsonPath / matchesXPath
({"expression": "$.status", "equalTo": "open"}), which selects a value
and checks it with the nested matcher; and XMLUnit’s options on
equalToXml:

  • enablePlaceholders — an expected value ${xmlunit.ignore} accepts
    anything, ${xmlunit.isNumber} any number, ${xmlunit.isDateTime} any
    date, ${xmlunit.matchesRegex(ORD-\d+)} a regular expression;
    placeholderOpeningDelimiterRegex / placeholderClosingDelimiterRegex
    change the ${ } delimiters;
  • ignoreOrderOfSameNode — sibling elements may come in any order;
  • exemptedComparisons — XMLUnit comparisons that are skipped, for example
    ["TEXT_VALUE", "SCHEMA_LOCATION"];
  • namespaceAwareness: NONE — namespaces are not compared.

Namespace prefixes never matter in equalToXml, only the namespace
they stand for.

The translator returns a gaps array on import. Every unsupported or
unrecognised key above is reported there, so read it: each entry means
the imported stub is less strict than the one you wrote.

Admin endpoints we do not implement

POST /__admin/shutdown (instance lifecycle belongs to the deployment
owner). GET /__admin/requests carries the
canonical serve-event envelope (request{url, absoluteUrl, method, …},
response{status}, wasMatched) with the flat fields kept beside it; an
unknown matcher key in a journal filter is rejected with 400 rather than
silently ignored.

postServeActions with the webhook action fire after the stub responds
(non-webhook extensions are reported as import gaps). Other custom
post-serve extensions are stored for round-trip only.

Response definition

  • status, body, jsonBody, base64Body, headers — all supported.

  • bodyFileName — a file uploaded with PUT /__admin/files/<name>.

  • fixedDelayMilliseconds — a fixed delay before the answer.

  • delayDistribution — a random delay drawn for every request, added to
    the fixed one: {"type": "lognormal", "median": 90, "sigma": 0.4}
    (optionally capped with maxValue), {"type": "uniform", "lower": 300, "upper": 500} or {"type": "fixed", "milliseconds": 200}.

  • chunkedDribbleDelay — {"numberOfChunks": 5, "totalDuration": 1000}
    sends the body in five pieces spread over one second, the way a slow
    network delivers it. The first piece arrives at once.

  • proxyBaseUrl — the request is forwarded to the base URL with its path
    and query: proxyBaseUrl: "https://api.example.com/v2" sends
    /users/7?x=1 to https://api.example.com/v2/users/7?x=1.
    proxyUrlPrefixToRemove cuts a leading path first,
    additionalProxyRequestHeaders adds headers (an API key, for example)
    and removeProxyRequestHeaders drops caller headers. The target must be
    reachable from the Mockarty server; private and loopback addresses are
    refused unless the operator allows them.

  • transformers: ["response-template"] — the body (including a
    bodyFileName file) and the response headers are Handlebars templates
    rendered for every request:

    • request data — request.method, request.url, request.path,
      request.pathSegments.[N], request.path.<var> (a urlPathTemplate
      variable), request.host, request.port, request.scheme,
      request.baseUrl, request.clientIp, request.body,
      request.bodyAsBase64, request.headers.X (any letter case),
      request.query.X, request.cookies.X; .first and .[N] pick one
      value of a repeated header or parameter;
    • blocks — {{#each}} (with as |item index|, @index, @first,
      @last), {{#if}} / {{else}}, {{#unless}}, {{#with}};
    • helpers — jsonPath, regexExtract (a third argument stores the
      groups in a variable), randomValue length=N type='…' (NUMERIC,
      ALPHABETIC, HEXADECIMAL, UUID), randomInt lower= upper=,
      now offset='3 days' format='yyyy-MM-dd' timezone='…',
      parseDate, date, size, base64, plus comparison and string
      helpers (eq, and, or, not, upper, lower, split, join).
      Date formats are Java patterns (dd/MM/yyyy HH:mm) or epoch /
      unix.
    {"request": {"method": "POST", "urlPathTemplate": "/orders/{id}"},
     "response": {"status": 200, "transformers": ["response-template"],
       "jsonBody": {"id": "{{request.path.id}}",
                    "items": "{{#each (jsonPath request.body '$.items') as |i|}}{{i.sku}} {{/each}}"}}}
    
  • fault — all four kinds supported: EMPTY_RESPONSE,
    MALFORMED_RESPONSE_CHUNK, RANDOM_DATA_THEN_CLOSE,
    CONNECTION_RESET_BY_PEER.

Recording

Start a recording with the real service as the target, send traffic
through the recording proxy, then stop — every recorded exchange becomes
a stub that answers what the real service answered (status, headers and
body):

curl -X POST "$MOCKARTY/__admin/recordings/start" \
  -H "Authorization: Bearer $MOCKARTY_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"targetBaseUrl": "https://api.example.com"}'
curl "$MOCKARTY/__admin/recordings/proxy/orders" \
  -H "Authorization: Bearer $MOCKARTY_API_TOKEN"
curl -X POST "$MOCKARTY/__admin/recordings/stop" \
  -H "Authorization: Bearer $MOCKARTY_API_TOKEN"
  • Traffic is recorded through /__admin/recordings/proxy/<path>, which
    forwards to <targetBaseUrl>/<path>.
  • repeatsAsScenarios (on by default): a request recorded several times
    with different answers becomes a scenario that replays the answers in
    order — the third call gets the third answer, later calls keep getting
    the last one. Set it to false to keep only the first answer.
  • allowNonProxied: true also records requests your existing stubs
    answered (with the answer that stub gives). Off by default.
  • captureHeaders turns the listed request headers into matchers;
    persist: true marks the recorded stubs persistent.

Scenarios (stateful behaviour)

WireMock scenarios round-trip end-to-end:

  • scenarioName, requiredScenarioState, newScenarioState on a stub
    are honoured at request time.
  • GET /__admin/scenarios lists scenarios discovered from imported stubs.
  • POST /__admin/scenarios/reset returns every scenario to Started.
  • PUT /__admin/scenarios/{name}/state forces a state.

Persistent stubs

Stubs imported with persistent: true survive
POST /__admin/mappings/reset — matches WireMock 3.x semantics.

Out of scope

  • transformers: ["<custom Java class>"] — only response-template.
  • POST /__admin/shutdown — instance lifecycle stays with the operator.

Step-by-step migration

  1. Spin up Mockarty and create or pick the namespace that will host
    the migrated stubs.

  2. Mint an API token in Mockarty (Settings → API Tokens) scoped to
    that namespace.

  3. Enable WireMock compatibility for the namespace.

  4. Repoint your clients: change the WireMock URL to your Mockarty
    admin URL and add the API token header.

  5. Bulk import your existing stubs:

    curl -X POST "$MOCKARTY_URL/__admin/mappings/import" \
      -H "X-API-Key: $TOKEN" \
      -H "Content-Type: application/json" \
      --data-binary "@wiremock-stubs.json"
    
  6. Inspect the response for any gaps entries — those are the only
    places you may need to adjust the original stub JSON.

  7. Review the imported stubs in the Mockarty UI — they appear in the
    namespace’s mock list with the wm_ ID prefix and can be edited the
    same way as native mocks.

Verifying the migration

Run the same WireMock-driven tests against the Mockarty URL. They should
pass with no further changes — every documented matcher kind, scenarios,
faults, body files and response templating are honoured. The gaps you
get at import time are the contract for what might behave differently.