WireMock
WireMock
WireMock is an HTTP mock server for testing. It starts an actual HTTP server that responds to configured request patterns with pre-defined responses. Unlike Mockito (which mocks Java objects), WireMock mocks real HTTP interactions — the application under test makes actual network calls to the WireMock server.
Core Use Cases
- Integration testing: stub all downstream HTTP services so integration tests run without external dependencies
- Contract testing: Spring Cloud Contract generates WireMock stubs from consumer contracts
- Fault injection: simulate timeouts, slow responses, connection resets, error status codes
- Record and replay: record real traffic from a service, replay it in tests
How It Works
Test Application Under Test WireMock Server
│ │ │
├─ configure stubs ────────────────────────────────▶ │
│ │ │
├─ call application │ │
│ (e.g. WebTestClient) ─▶ │ │
│ ├─ HTTP GET /users/42 ───▶ │
│ │◀─ 200 {"id":42,...} ─────┤
│ │ │
│◀─ aggregated response ───┤ │
│ │ │
├─ verify stubs ────────────────────────────────────▶ │
The application calls localhost:<wiremock-port> — the test configures WireMock to intercept those calls. The application's HTTP client does not know it is talking to a stub.
Spring Boot 3 Integration
Correct Library: wiremock-spring-boot
For Spring Boot 3.x, use org.wiremock.integrations:wiremock-spring-boot. Do not use the legacy WireMockRule (JUnit 4) or manual WireMockServer lifecycle management — these work but require more boilerplate.
// build.gradle
testImplementation 'org.wiremock.integrations:wiremock-spring-boot:3.5.4'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test' // for WebFlux / StepVerifier@WireMockTest Annotation
@WireMockTest starts a WireMock server for the test class and shuts it down after. It resets all stubs between tests automatically.
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
import org.junit.jupiter.api.Test;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
@WireMockTest(httpPort = 8090)
class MyServiceTest {
@Test
void shouldCallDownstream(WireMockRuntimeInfo wmInfo) {
// Configure stub
stubFor(get(urlEqualTo("/resource/1"))
.willReturn(okJson("""{"id":1,"name":"Item One"}""")));
// ... call your application which calls localhost:8090 ...
// Verify the stub was called
verify(getRequestedFor(urlEqualTo("/resource/1")));
}
}httpPort = 8090 uses a fixed port. Use @WireMockTest without a port for random port (access via wmInfo.getPort()).
Integration with @SpringBootTest
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
@ActiveProfiles("test")
@WireMockTest(httpPort = 8090)
class FullStackIntegrationTest {
@Autowired
private WebTestClient webTestClient;
@Test
void shouldAggregateData() {
stubFor(get(urlEqualTo("/users/42"))
.willReturn(okJson("""{"id":42,"name":"Jane"}""")));
stubFor(get(urlPathEqualTo("/orders"))
.withQueryParam("userId", equalTo("42"))
.willReturn(okJson("[{\"id\":\"ord-1\"}]")));
webTestClient.get()
.uri("/api/dashboard/42")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.user.name").isEqualTo("Jane")
.jsonPath("$.orders[0].id").isEqualTo("ord-1");
}
}application-test.yml must point the application's downstream URLs at localhost:8090:
# src/test/resources/application-test.yml
services:
user-service:
base-url: http://localhost:8090
order-service:
base-url: http://localhost:8090Stub Configuration API
Static Import
import static com.github.tomakehurst.wiremock.client.WireMock.*;Request Matching
// Exact URL
stubFor(get(urlEqualTo("/users/42")));
// URL path (ignores query params)
stubFor(get(urlPathEqualTo("/users")));
// URL pattern (regex)
stubFor(get(urlMatching("/users/[0-9]+")));
// URL path + query params
stubFor(get(urlPathEqualTo("/orders"))
.withQueryParam("userId", equalTo("42"))
.withQueryParam("status", matching("(OPEN|PENDING)")));
// POST with body matching
stubFor(post(urlEqualTo("/users"))
.withRequestBody(matchingJsonPath("$.name", equalTo("Jane")))
.withRequestBody(matchingJsonPath("$.email", matching(".+@.+"))));
// Header matching
stubFor(get(urlEqualTo("/secure/resource"))
.withHeader("Authorization", matching("Bearer .+")));Response Building
// JSON response (sets Content-Type: application/json)
stubFor(get(urlEqualTo("/users/42"))
.willReturn(okJson("""{"id":42,"name":"Jane"}""")));
// Custom status
stubFor(get(urlEqualTo("/users/999"))
.willReturn(notFound()
.withHeader("Content-Type", "application/json")
.withBody("""{"error":"Not found"}""")));
// Server error
stubFor(get(urlEqualTo("/flaky"))
.willReturn(serverError()));
// Custom response
stubFor(get(urlEqualTo("/custom"))
.willReturn(aResponse()
.withStatus(202)
.withHeader("X-Custom", "value")
.withBody("accepted")));
// Add latency (for timeout testing)
stubFor(get(urlEqualTo("/slow"))
.willReturn(okJson("""{"data":"late"}""")
.withFixedDelay(5000))); // 5 second delay
// Fault simulation
stubFor(get(urlEqualTo("/broken"))
.willReturn(aResponse()
.withFault(Fault.CONNECTION_RESET_BY_PEER)));Stateful Stubs (Scenarios)
WireMock supports stateful stub sequences — useful for testing retry and circuit breaker logic:
// First two calls fail, third succeeds
stubFor(get(urlEqualTo("/flaky"))
.inScenario("Flaky Service")
.whenScenarioStateIs(STARTED)
.willReturn(serverError())
.willSetStateTo("First Failure"));
stubFor(get(urlEqualTo("/flaky"))
.inScenario("Flaky Service")
.whenScenarioStateIs("First Failure")
.willReturn(serverError())
.willSetStateTo("Second Failure"));
stubFor(get(urlEqualTo("/flaky"))
.inScenario("Flaky Service")
.whenScenarioStateIs("Second Failure")
.willReturn(okJson("""{"status":"ok"}""")));Verification API
After the test, verify downstream was called correctly:
// Called exactly once
verify(1, getRequestedFor(urlEqualTo("/users/42")));
// Called at least once
verify(moreThanOrExactly(1), getRequestedFor(urlEqualTo("/users/42")));
// Called with specific headers
verify(getRequestedFor(urlEqualTo("/users/42"))
.withHeader("Authorization", containing("Bearer"))
.withHeader("X-Correlation-ID", matching("[a-f0-9-]{36}")));
// Never called
verify(0, getRequestedFor(urlEqualTo("/admin/delete")));
// Called with specific body
verify(postRequestedFor(urlEqualTo("/events"))
.withRequestBody(matchingJsonPath("$.type", equalTo("USER_VIEWED"))));WireMock and Spring Cloud Contract
Spring Cloud Contract uses WireMock as its stub format. When the contract plugin runs, it generates WireMock JSON stub files from the Groovy/YAML contract definitions. These stubs are packaged into a stub JAR that consumers load via @AutoConfigureStubRunner.
The consumer never writes WireMock stubs manually when using Spring Cloud Contract — they write contracts, and stubs are generated. But knowing WireMock is useful for:
- Understanding what the generated stubs do
- Writing ad-hoc integration tests that don't need a full contract workflow
- Testing error paths that aren't in the contracts
See Consumer-Driven-Contract-Testing for the full contract workflow.
WireMock vs. Mockito
| Aspect | WireMock | Mockito |
|---|---|---|
| What it mocks | HTTP server (real network call) | Java objects (in-process) |
| Use case | Integration tests of HTTP clients | Unit tests of service logic |
| Setup | Starts actual HTTP server | No infrastructure |
| Verifies | HTTP requests (method, URL, headers, body) | Method calls on mock objects |
| Fault injection | Yes (network-level) | No |
| Speed | Slower (I/O) | Faster (in-memory) |
Both are necessary: Mockito for unit tests, WireMock for integration tests.
BFF Integration Testing
In a BFF-Pattern integration test, WireMock stubs all downstream microservices. The test exercises:
- The incoming request reaching the BFF
- The BFF's filter chain (authentication, correlation ID, tracing)
- The BFF's aggregation logic (parallel WebClient calls)
- WireMock receiving those downstream calls
- The BFF composing the response from stub responses
- The composed response returned to the test client
This is a full test of the BFF's behaviour without any real downstream services.
See P5-BFF-Observability-Testing — TEST-02 for complete BFF WireMock integration test examples.
Related Concepts
- Consumer-Driven-Contract-Testing — Spring Cloud Contract generates WireMock stubs
- BFF-Pattern — BFF integration tests use WireMock to stub all downstream services
- Spring-Cloud-Gateway — the gateway being tested with WireMock stubs
- Project-Reactor —
StepVerifiercomplements WireMock for reactive assertions