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:8090

Stub 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

AspectWireMockMockito
What it mocksHTTP server (real network call)Java objects (in-process)
Use caseIntegration tests of HTTP clientsUnit tests of service logic
SetupStarts actual HTTP serverNo infrastructure
VerifiesHTTP requests (method, URL, headers, body)Method calls on mock objects
Fault injectionYes (network-level)No
SpeedSlower (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:

  1. The incoming request reaching the BFF
  2. The BFF's filter chain (authentication, correlation ID, tracing)
  3. The BFF's aggregation logic (parallel WebClient calls)
  4. WireMock receiving those downstream calls
  5. The BFF composing the response from stub responses
  6. 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.