Skip to main content
Back to Blog
API Testing
2026-05-20

WireMock API Mocking Complete Guide 2026

Master WireMock for API mocking and service virtualization. Stubbing, request matching, dynamic responses, recording, Docker setup, and CI integration.

WireMock API Mocking Complete Guide 2026

WireMock is the most widely used HTTP mock server in the Java ecosystem and one of the most popular across all stacks. It lets you stand up a fake version of any HTTP service - third-party APIs, downstream microservices, payment gateways, identity providers - and program it to respond in any way you need. With WireMock, you can write integration tests that don't depend on external systems being available, simulate edge cases like 500 errors or slow responses, record real traffic to generate stubs, and run service virtualization in pre-prod environments.

This complete guide covers every aspect of WireMock in 2026: installation as a standalone server, as a Java library, and as a Docker container; request matching by URL, method, headers, body, and JSON path; stubbed response generation with templating and proxies; recording real traffic; stateful behavior; CI integration; and patterns for testing microservices end-to-end. Real code examples and configuration JSON are included. By the end you'll be ready to use WireMock to make your integration tests deterministic and fast.

Key Takeaways

  • WireMock is a polyglot HTTP mock server, runs standalone or in-process
  • Supports request matching by URL, method, headers, body, JSON path, XPath
  • Dynamic responses via templating (Handlebars) and proxying
  • Record real traffic to generate stubs automatically
  • Stateful scenarios for multi-step behavior
  • Docker image for easy CI integration
  • WireMock Cloud (paid) for hosted mocking

Installation

Standalone JAR

wget https://github.com/wiremock/wiremock/releases/download/3.3.0/wiremock-standalone-3.3.0.jar
java -jar wiremock-standalone-3.3.0.jar --port 8080

Docker

docker run -p 8080:8080 wiremock/wiremock:3.3.0

Java Library

<dependency>
  <groupId>org.wiremock</groupId>
  <artifactId>wiremock</artifactId>
  <version>3.3.0</version>
  <scope>test</scope>
</dependency>

Basic Stub

Create a JSON file in the mappings directory:

{
  "request": {
    "method": "GET",
    "url": "/users/1"
  },
  "response": {
    "status": 200,
    "headers": {"Content-Type": "application/json"},
    "jsonBody": {"id": 1, "name": "Alice"}
  }
}

Now curl http://localhost:8080/users/1 returns the stubbed response.

Stubs Via API

curl -X POST http://localhost:8080/__admin/mappings -d '{
  "request": {"method": "GET", "url": "/users/2"},
  "response": {"status": 200, "jsonBody": {"id": 2, "name": "Bob"}}
}'

Java DSL

import com.github.tomakehurst.wiremock.WireMockServer;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import org.junit.jupiter.api.*;

class WireMockTest {
    static WireMockServer wireMockServer;

    @BeforeAll
    static void setup() {
        wireMockServer = new WireMockServer(8080);
        wireMockServer.start();
    }

    @AfterAll
    static void teardown() {
        wireMockServer.stop();
    }

    @Test
    void getUserReturnsAlice() {
        stubFor(get(urlEqualTo("/users/1"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"id\": 1, \"name\": \"Alice\"}")));

        // Call your client against http://localhost:8080
        // Assert behavior
    }
}

URL Matching

MatcherExample
urlEqualTourlEqualTo("/users/1")
urlPathEqualTourlPathEqualTo("/users/1") (ignores query)
urlPathMatchingurlPathMatching("/users/\d+")
urlMatchingurlMatching("/users\?.*")

Method Matching

stubFor(post(urlEqualTo("/orders"))
    .willReturn(aResponse().withStatus(201)));

stubFor(put(urlPathMatching("/users/\\d+"))
    .willReturn(aResponse().withStatus(200)));

Header Matching

stubFor(get("/me")
    .withHeader("Authorization", containing("Bearer "))
    .willReturn(aResponse().withStatus(200)));

stubFor(get("/me")
    .withHeader("Authorization", absent())
    .willReturn(aResponse().withStatus(401)));

Body Matching

stubFor(post("/users")
    .withRequestBody(equalToJson("{\"name\": \"Alice\"}"))
    .willReturn(aResponse().withStatus(201)));

stubFor(post("/users")
    .withRequestBody(matchingJsonPath("$.email"))
    .willReturn(aResponse().withStatus(201)));

stubFor(post("/users")
    .withRequestBody(matchingXPath("//user/email"))
    .willReturn(aResponse().withStatus(201)));

Response Templating

Enable Handlebars-style templating:

java -jar wiremock-standalone.jar --global-response-templating
{
  "request": {"method": "GET", "urlPathPattern": "/users/(.+)"},
  "response": {
    "status": 200,
    "jsonBody": {
      "id": "{{request.path.[1]}}",
      "name": "User {{request.path.[1]}}"
    }
  }
}

GET /users/42 returns {"id": "42", "name": "User 42"}.

Stateful Scenarios

stubFor(get("/cart")
    .inScenario("Cart Flow")
    .whenScenarioStateIs(STARTED)
    .willReturn(aResponse().withJsonBody("[]"))); // empty cart

stubFor(post("/cart/items")
    .inScenario("Cart Flow")
    .whenScenarioStateIs(STARTED)
    .willSetStateTo("Item Added")
    .willReturn(aResponse().withStatus(201)));

stubFor(get("/cart")
    .inScenario("Cart Flow")
    .whenScenarioStateIs("Item Added")
    .willReturn(aResponse().withJsonBody("[{\"sku\": \"ABC\"}]")));

Delays And Faults

Simulate slow responses:

stubFor(get("/slow")
    .willReturn(aResponse()
        .withStatus(200)
        .withFixedDelay(5000))); // 5 second delay

Random delay:

stubFor(get("/users").willReturn(aResponse().withUniformRandomDelay(50, 500)));

Simulate network errors:

stubFor(get("/broken")
    .willReturn(aResponse()
        .withFault(Fault.EMPTY_RESPONSE)));

Fault options: EMPTY_RESPONSE, MALFORMED_RESPONSE_CHUNK, RANDOM_DATA_THEN_CLOSE, CONNECTION_RESET_BY_PEER.

Proxying

Forward unmatched requests to a real upstream:

stubFor(any(anyUrl()).willReturn(aResponse().proxiedFrom("https://real-api.example.com")));

Recording

Record real traffic to generate stubs:

java -jar wiremock-standalone.jar --proxy-all="https://api.example.com" --record-mappings

Make some real calls through localhost:8080, and WireMock writes the request/response pairs as stubs.

Request Verification

verify(postRequestedFor(urlEqualTo("/users"))
    .withRequestBody(equalToJson("{\"name\": \"Alice\"}")));

verify(2, getRequestedFor(urlPathEqualTo("/me")));

Reset

Between tests, reset state:

@BeforeEach
void clearStubs() {
    wireMockServer.resetAll();
}

Or via HTTP: POST /__admin/reset

CI Integration With Docker

# .github/workflows/test.yml
jobs:
  integration:
    runs-on: ubuntu-latest
    services:
      wiremock:
        image: wiremock/wiremock:3.3.0
        ports:
          - 8080:8080
        volumes:
          - ./wiremock-mappings:/home/wiremock/mappings
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
      - run: mvn test -Dwiremock.url=http://localhost:8080

Docker Compose

services:
  app:
    build: .
    environment:
      PAYMENT_API_URL: http://wiremock:8080
    depends_on: [wiremock]
  wiremock:
    image: wiremock/wiremock:3.3.0
    ports: ['8080:8080']
    volumes:
      - ./mocks:/home/wiremock/mappings

Real Test Example

class OrderServiceTest {
    @RegisterExtension
    static WireMockExtension wireMock = WireMockExtension.newInstance()
        .options(WireMockConfiguration.options().port(8089))
        .build();

    @Test
    void createOrderHitsPaymentService() {
        wireMock.stubFor(post("/charges")
            .withRequestBody(matchingJsonPath("$.amount"))
            .willReturn(aResponse()
                .withStatus(200)
                .withBody("{\"status\": \"paid\", \"id\": \"ch_abc\"}")));

        OrderService service = new OrderService("http://localhost:8089");
        OrderResult result = service.createOrder(new Order("ABC", 2));

        assertEquals("paid", result.getPaymentStatus());

        wireMock.verify(postRequestedFor(urlEqualTo("/charges"))
            .withRequestBody(matchingJsonPath("$.amount", equalTo("19.98"))));
    }

    @Test
    void handlesPaymentTimeout() {
        wireMock.stubFor(post("/charges")
            .willReturn(aResponse().withStatus(200).withFixedDelay(10000)));

        OrderService service = new OrderService("http://localhost:8089");
        assertThrows(TimeoutException.class, () -> service.createOrder(new Order("ABC", 1)));
    }

    @Test
    void retriesOnTransientFault() {
        wireMock.stubFor(post("/charges")
            .inScenario("Retry")
            .whenScenarioStateIs(STARTED)
            .willSetStateTo("Second Try")
            .willReturn(aResponse().withFault(Fault.CONNECTION_RESET_BY_PEER)));

        wireMock.stubFor(post("/charges")
            .inScenario("Retry")
            .whenScenarioStateIs("Second Try")
            .willReturn(aResponse().withStatus(200).withBody("{\"status\": \"paid\"}")));

        OrderService service = new OrderService("http://localhost:8089");
        OrderResult result = service.createOrder(new Order("ABC", 1));
        assertEquals("paid", result.getPaymentStatus());

        wireMock.verify(2, postRequestedFor(urlEqualTo("/charges")));
    }
}

Comparison To Alternatives

ToolLanguageBest For
WireMockJava/standaloneJVM teams, polyglot via HTTP
MockoonStandalone GUIQuick mocks, no code
nockNode JSNode test mocking
PactPolyglotContract testing
MSWJS browser/nodeBrowser fetch mocking
HoverflyGo standaloneService virtualization at scale

When To Use WireMock

  • Mocking third-party APIs in integration tests
  • Service virtualization across teams
  • Testing edge cases (timeouts, 500s, malformed responses)
  • Recording real traffic to seed tests
  • Stateful microservice testing

When Not To Use WireMock

  • Unit tests where a simple mock library suffices
  • Browser-only testing (use MSW)
  • High-throughput load testing (use real services or load test tools)

CI Patterns

PatternUse Case
WireMock as sidecarLong-running integration tests
WireMock in test setupPer-test isolation
Recorded stubs in gitStable, reviewable mocks
WireMock CloudDistributed mocking

Anti-Patterns

Anti-PatternBetter
One mega mappings fileSplit by domain
Recording into prod stubsCurate recorded mappings
No reset between testsresetAll() in @BeforeEach
Mocking your own serviceTest it directly
Stale stubsAuto-regenerate from contracts

Conclusion

WireMock is the gold standard for HTTP mocking in the JVM ecosystem and a strong contender across all stacks via its standalone server and Docker image. The combination of flexible request matching, dynamic responses, stateful scenarios, recording, and fault injection covers virtually every API mocking need. For teams writing integration tests against third-party services or microservices, it's an essential tool.

Start by adding WireMock to one test that depends on an external API. Replace the real URL with localhost:8089 and stub the expected behavior. Run the test - it should pass without network access. From there, build out your mock library incrementally. Visit our skills directory or the API mocking service virtualization guide for related patterns.

WireMock API Mocking Complete Guide 2026 | QASkills.sh