Skip to content
End To End Tester

WireMock

A programmable HTTP server for tests — stubbing responses, injecting failures, verifying requests, and recording real traffic to replay.

1 min read · updated 19 September 2026

WireMock is an HTTP server you program from your tests. It serves the responses you specify, records the requests it received, and can be told to fail in ways no real service will fail on demand.

It is the standard answer to the question "how do I test what happens when the payment provider returns a 503".

#Stubbing

java
// Java, JUnit 5
@WireMockTest
class RatesClientTest {

    @Test
    void parsesASuccessfulResponse(WireMockRuntimeInfo wm) {
        stubFor(get(urlPathEqualTo("/v1/rates"))
            .withQueryParam("base", equalTo("USD"))
            .withHeader("Accept", equalTo("application/json"))
            .willReturn(okJson("""
                { "base": "USD", "rates": { "GBP": 0.79 } }
                """)));

        var client = new RatesClient(wm.getHttpBaseUrl());

        assertThat(client.gbpRate()).isEqualTo(0.79);
    }
}
javascript
// The standalone server has an HTTP admin API, usable from any language.
await fetch('http://localhost:8080/__admin/mappings', {
  method: 'POST',
  body: JSON.stringify({
    request: { method: 'GET', urlPath: '/v1/rates' },
    response: { status: 200, jsonBody: { rates: { GBP: 0.79 } } }
  })
});
csharp
// C#, WireMock.Net
var server = WireMockServer.Start();

server.Given(Request.Create().WithPath("/v1/rates").UsingGet())
      .RespondWith(Response.Create().WithStatusCode(200).WithBodyAsJson(new { rates = new { GBP = 0.79 } }));

var client = new RatesClient(server.Urls[0]);

#Fault injection — the part that earns its keep

java
// Status codes
stubFor(get("/v1/rates").willReturn(aResponse().withStatus(503)));

// Rate limiting, with the header your retry logic is supposed to read
stubFor(get("/v1/rates").willReturn(aResponse()
    .withStatus(429)
    .withHeader("Retry-After", "120")));

// Slow enough to cross your timeout
stubFor(get("/v1/rates").willReturn(aResponse().withFixedDelay(5_000).withStatus(200)));

// Connection-level failures
stubFor(get("/v1/rates").willReturn(aResponse().withFault(Fault.CONNECTION_RESET_BY_PEER)));
stubFor(get("/v1/rates").willReturn(aResponse().withFault(Fault.EMPTY_RESPONSE)));
stubFor(get("/v1/rates").willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK)));

Every one of those happens in production. Almost none of them are covered by a suite that only talks to the real service, because the real service will not cooperate.

#Scenarios — stateful stubs

For testing retry and recovery, where the second call must behave differently from the first:

java
stubFor(get("/v1/rates").inScenario("flaky")
    .whenScenarioStateIs(STARTED)
    .willReturn(aResponse().withStatus(503))
    .willSetStateTo("recovered"));

stubFor(get("/v1/rates").inScenario("flaky")
    .whenScenarioStateIs("recovered")
    .willReturn(okJson("{\"rates\":{\"GBP\":0.79}}")));

// The client should retry once and succeed.
assertThat(client.gbpRate()).isEqualTo(0.79);
verify(2, getRequestedFor(urlPathEqualTo("/v1/rates")));

#Verifying requests

java
verify(postRequestedFor(urlPathEqualTo("/v1/charges"))
    .withHeader("Idempotency-Key", matching("^ORD-.*"))
    .withRequestBody(matchingJsonPath("$.amount", equalTo("4000"))));

// Nothing unexpected was sent
verify(0, postRequestedFor(urlPathEqualTo("/v1/refunds")));

Verifying the idempotency key is a good example of a test that only this level can write: it is invisible to a unit test with a mocked client and irrelevant to an end-to-end test.

#Record and replay

The cure for hand-written stubs that encode a wrong belief:

bash
# Proxy to the real service and record everything that goes through
java -jar wiremock-standalone.jar \
  --port 8080 \
  --proxy-all="https://api.example.com" \
  --record-mappings --verbose

Point your app at localhost:8080, exercise the flows once, and WireMock writes the mappings and response bodies to disk. Replay them from then on. At least the fiction started as fact — see integration testing with stubs for the drift discussion.

#Running it

In-process for a single test suite — fastest, no separate lifecycle.

Standalone jar when several suites or several languages share stubs.

As a container, which composes with Testcontainers:

java
@Container
static final WireMockContainer WIREMOCK = new WireMockContainer("wiremock/wiremock:3.9.1")
    .withMappingFromResource("rates", "stubs/rates.json");

Always use a dynamic port. options().dynamicPort() — a fixed port is the most common reason a suite cannot run in parallel.

#Where it stops

WireMock tests your side of the contract. It cannot tell you the provider still behaves the way your stubs say. For services inside your organisation that is what contract testing is for; for third parties, re-record periodically and keep a small suite that hits the real sandbox on a schedule, outside the blocking pipeline.

Common questions

When should I use WireMock instead of mocking the HTTP client?
Whenever you want your real client code to run. Mocking the client skips serialization, timeouts, retries, connection handling and error mapping — which is where the bugs are. WireMock fakes only the far side of the socket.
How do I keep WireMock stubs from drifting away from the real API?
Record them from the real service rather than writing them by hand, re-record periodically, and validate responses against the provider's OpenAPI schema if there is one. For services inside your organisation, contract testing solves it properly.
Can WireMock simulate a slow or broken connection?
Yes — fixed and random delays, chunked dribble, malformed responses, connection resets and empty responses. Fault injection is the main thing it can do that a real dependency cannot.

Runnable samples for this page

last test results ↗
  • Javajava/src/test/java/tools/wiremock

Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.

Was this page useful?