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, 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);
}
}// 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 } } }
})
});// 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
// 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:
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
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:
# 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 --verbosePoint 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:
@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 ↗- Java
java/src/test/java/tools/wiremock
Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.
Was this page useful?
Related topics
- Integration Testing with StubsTesting against a dependency you control — WireMock, MSW and in-process servers — so error paths, timeouts and rate limits become testable instead of theoretical.
- Contract TestingProving two services still agree without deploying both — consumer-driven contracts with Pact, provider verification in CI, and where contract testing beats end-to-end.
- API TestingTesting HTTP and message interfaces directly — the level most teams under-invest in relative to its value, and how it replaces most slow end-to-end tests.
- TestcontainersRunning real databases, brokers and services as disposable containers from inside your test suite — the pattern that made integration testing cheap.
- Flaky TestsWhy tests fail intermittently, the six root causes and how to fix each one, how to detect flakiness deliberately, and what to do with a test you cannot fix today.
- Terraform for Test InfrastructureProvisioning ephemeral test environments as code — per-branch stacks, workspaces, cost controls, and testing the Terraform itself.
- Writing Tests in JavaJUnit 5, Mockito, AssertJ and Testcontainers — the stack that has been stable for a decade, plus Spring's test slices and what they cost.
- Integration TestingTesting your code against real dependencies — databases, HTTP clients, message brokers — with Testcontainers, and what belongs at this level rather than above or below it.