Back to guides

GUIDE / api

API Mocking with WireMock: Stubs, Tests, and Examples

API mocking with WireMock guide for QA teams covering stubs, request matching, delays, faults, recording, contracts, CI, and mistakes.

By The Testing AcademyPublished July 10, 2026Updated July 10, 202614 min read

API mocking with WireMock helps QA and engineering teams test systems before every dependency is stable, available, cheap, or easy to control. Real APIs are important, but they can be slow, flaky, costly, rate-limited, or impossible to force into rare error states. WireMock gives you a controlled HTTP server that behaves like the dependency you need for a specific test.

This guide explains how to use WireMock for practical API testing. You will learn what to mock, how stubs work, how request matching works, how to simulate failures, how to record APIs, how to use WireMock in CI, and how to avoid the dangerous mistake of trusting mocks that no longer match reality.

API Mocking with WireMock: The Practical Use Case

API mocking with WireMock is useful when your system under test calls another HTTP service and you need predictable responses. Instead of depending on the real service, you run WireMock locally, define request-response stubs, point your application to the mock URL, and verify how your application behaves.

Use WireMock when:

  • A dependency is not built yet.
  • A third-party service is unstable or rate-limited.
  • You need to test rare errors.
  • You need deterministic responses.
  • You want fast local integration tests.
  • You need to simulate latency, timeouts, or malformed responses.
  • You want to isolate the system under test.

Do not use WireMock to avoid real integration forever. Mock tests prove how your system behaves against expected dependency behavior. Real integration tests prove that the dependency actually behaves that way. Both are needed.

For broader framework context, see API automation framework. WireMock is one layer inside a healthy API testing strategy.

What WireMock Does

WireMock is an HTTP mock server. It receives requests and returns responses based on rules you define. A rule is called a stub. A stub usually includes request matching and response definition.

Example concept:

{
  "request": {
    "method": "GET",
    "url": "/customers/123"
  },
  "response": {
    "status": 200,
    "headers": {
      "Content-Type": "application/json"
    },
    "jsonBody": {
      "id": "123",
      "status": "ACTIVE"
    }
  }
}

When your application calls GET /customers/123, WireMock returns that JSON. Your application believes it called the dependency. Your test can now verify the application behavior under a known dependency response.

WireMock can match by:

  • HTTP method.
  • URL path.
  • Query parameters.
  • Headers.
  • Cookies.
  • Request body.
  • JSONPath.
  • XPath.
  • Basic auth.
  • Multipart data.

It can return:

  • Status codes.
  • Headers.
  • JSON.
  • XML.
  • Plain text.
  • Files.
  • Delays.
  • Faults.
  • Scenario-based responses.

That flexibility makes WireMock useful for more than simple happy paths.

WireMock vs Real API Testing

Mocking and real API testing answer different questions.

QuestionWireMock TestReal API Test
Can my service handle a 500 from dependency?Strong fitHard to force safely
Does dependency return the documented schema?Only if stub is accurateStrong fit
Can my retry logic handle timeouts?Strong fitHard and risky
Does auth work between real systems?Weak fitStrong fit
Can I test before provider is ready?Strong fitNot possible
Does production-like integration work?Weak fitStrong fit

The mistake is choosing one side as "the truth." WireMock improves control. Real integration improves confidence. A mature test strategy uses both at the right stages.

Running WireMock

WireMock can run in several ways:

  • Java library inside tests.
  • Standalone JAR.
  • Docker container.
  • JUnit extension.
  • Managed service virtualization setup.

For local and CI usage, Docker is common:

docker run -it --rm \
  -p 8089:8080 \
  -v "$PWD/wiremock:/home/wiremock" \
  wiremock/wiremock

With this setup, stubs can live in wiremock/mappings and response files can live in wiremock/__files. Your application points dependency base URL to http://localhost:8089.

In Java tests, a JUnit extension can start and stop WireMock automatically. That is useful for isolated integration tests. In system tests, a Docker container may match the deployment workflow better.

Creating Your First Stub

Create a mapping file:

{
  "request": {
    "method": "GET",
    "url": "/inventory/SKU-123"
  },
  "response": {
    "status": 200,
    "headers": {
      "Content-Type": "application/json"
    },
    "body": "{\"sku\":\"SKU-123\",\"available\":true,\"quantity\":5}"
  }
}

Then call:

curl http://localhost:8089/inventory/SKU-123

WireMock returns the configured response.

For larger JSON bodies, store files under __files:

{
  "request": {
    "method": "GET",
    "url": "/inventory/SKU-123"
  },
  "response": {
    "status": 200,
    "headers": {
      "Content-Type": "application/json"
    },
    "bodyFileName": "inventory-available.json"
  }
}

This keeps mappings readable and response bodies easier to review.

Request Matching

Request matching is where mocks become useful or dangerous. If matching is too loose, the app can send the wrong request and still get a successful response. If matching is too strict, harmless differences break tests.

Match what matters:

  • Method.
  • Path.
  • Required query parameters.
  • Important headers.
  • Relevant body fields.

Example with query matching:

{
  "request": {
    "method": "GET",
    "urlPath": "/customers",
    "queryParameters": {
      "status": {
        "equalTo": "active"
      }
    }
  },
  "response": {
    "status": 200,
    "jsonBody": {
      "items": [
        { "id": "C-1", "status": "active" }
      ]
    }
  }
}

Example with JSON body matching:

{
  "request": {
    "method": "POST",
    "url": "/payments",
    "bodyPatterns": [
      { "matchesJsonPath": "$[?(@.amount == 1000)]" },
      { "matchesJsonPath": "$[?(@.currency == 'USD')]" }
    ]
  },
  "response": {
    "status": 201,
    "jsonBody": {
      "paymentId": "P-123",
      "status": "AUTHORIZED"
    }
  }
}

This verifies that the system under test sends important fields correctly.

Simulating Failures, Delays, and Edge Cases

WireMock is especially valuable for failure testing. Real dependencies may not let you trigger exact conditions safely.

Simulate:

  • HTTP 400 validation error.
  • HTTP 401 unauthorized.
  • HTTP 403 forbidden.
  • HTTP 404 not found.
  • HTTP 409 conflict.
  • HTTP 429 rate limit.
  • HTTP 500 server error.
  • Slow response.
  • Connection reset.
  • Malformed body.
  • Empty response.

Example delayed response:

{
  "request": {
    "method": "GET",
    "url": "/risk-score/C-1"
  },
  "response": {
    "status": 200,
    "fixedDelayMilliseconds": 5000,
    "jsonBody": {
      "score": 72
    }
  }
}

Now test whether your application times out, retries, shows a fallback, or blocks the user. For resilience testing, these scenarios are often more valuable than another happy path test.

Stateful Scenarios

Some flows need state. For example, the first call returns pending, the second returns complete. WireMock scenarios can model this.

{
  "scenarioName": "payment status",
  "requiredScenarioState": "Started",
  "newScenarioState": "Authorized",
  "request": {
    "method": "GET",
    "url": "/payments/P-123"
  },
  "response": {
    "status": 200,
    "jsonBody": {
      "status": "PENDING"
    }
  }
}

A second stub can require state Authorized and return AUTHORIZED.

Use state sparingly. Stateful mocks can become harder to reason about, especially in parallel tests. Reset WireMock state between tests.

Recording and Playback

WireMock can record traffic from a real API and create stubs. This is useful when learning a dependency or bootstrapping stubs quickly.

Recording is not the final answer. Recorded stubs may include sensitive data, unnecessary headers, dynamic IDs, timestamps, and overly specific matching. Review and clean them.

After recording:

  • Remove secrets.
  • Replace personal data.
  • Generalize dynamic values.
  • Tighten important matchers.
  • Delete irrelevant headers.
  • Add error scenarios manually.
  • Align with contracts or documentation.

Recorded stubs are raw material, not finished test design.

WireMock in CI

WireMock works well in CI when it starts predictably and the application under test receives the mock base URL through configuration.

CI checklist:

  • Start WireMock before tests.
  • Load mappings from version control.
  • Point dependency URL to WireMock.
  • Reset state between tests.
  • Expose request logs on failure.
  • Fail if unmatched requests occur.
  • Keep port configuration stable.
  • Avoid sharing one stateful WireMock server across parallel tests unless isolated.

Unmatched requests are important. If your application calls a URL with no stub, the test should fail loudly. Silent mock gaps hide integration problems.

Designing Good Stubs

Good stubs are small contracts. They should show the request that matters and the response the system must handle. A stub should not be a random dump of everything a real server returned once.

Start by naming the behavior:

  • inventory-available
  • inventory-out-of-stock
  • payment-authorized
  • payment-declined
  • customer-service-timeout
  • rate-limit-after-three-requests

Then match the important request details. If your application must send sku, warehouseId, and channel, match those fields. If a tracing header is irrelevant, do not match it. If authorization behavior matters, match the auth header or create a separate security-focused test.

Keep response bodies realistic. Include fields the application uses, not every field the provider can return. Also include fields that have caused defects before, such as null values, empty arrays, long names, and enum variations.

Review stubs like code. A bad stub can create a false pass. If the real provider returns availableQuantity but the stub returns quantity, the test may validate behavior that cannot happen in production. Contract review, schema validation, or periodic comparison against real responses helps prevent this.

Verifying Requests Sent to WireMock

WireMock can also verify that your application made the expected calls. This is useful when the behavior under test is not only the final UI or API response, but also the outbound integration.

For example, after a checkout attempt, verify that the application called the payment provider with the correct amount and currency:

verify(postRequestedFor(urlEqualTo("/payments"))
    .withRequestBody(matchingJsonPath("$.amount", equalTo("1000")))
    .withRequestBody(matchingJsonPath("$.currency", equalTo("USD"))));

Request verification catches integration mapping bugs. Your application might show a success message because the mock returned success, but it may have sent the wrong amount, missing metadata, or incorrect customer ID. That is a serious defect.

Use verification carefully. Do not assert every header and field unless they are part of the contract. Over-verification makes tests brittle. Focus on fields that affect business behavior, security, routing, and idempotency.

WireMock for Webhook and Callback Testing

WireMock is useful when your application receives or sends webhook-style callbacks. You can mock the external system that sends a callback, or you can stand up WireMock as the receiver and verify that your system called it.

For outgoing webhooks, point the webhook target URL to WireMock. Trigger the event in your application, then verify the callback request. Check event type, signature header, timestamp, idempotency key, and body fields.

For incoming webhooks, WireMock may mock the upstream API your application calls after receiving the webhook. This helps test retry and reconciliation behavior without relying on the external provider.

Webhook flows are often asynchronous, so combine WireMock verification with polling. Wait until the expected request is received or fail with the list of actual received requests. For deeper webhook test design, see how to test webhooks.

WireMock and Contract Testing

WireMock can support contract testing, but it does not automatically guarantee correctness. A stub is only as accurate as the source used to create it.

Ways to keep mocks honest:

  • Generate stubs from OpenAPI examples.
  • Validate stubs against JSON schema.
  • Use Pact contracts for consumer-provider expectations.
  • Run provider tests against contracts.
  • Periodically compare stubs with real API responses.
  • Review stubs during API changes.

If consumers and providers are owned by different teams, look at contract testing with Pact. Pact focuses on proving that provider behavior satisfies consumer expectations. WireMock focuses on simulating HTTP behavior.

Common Mistakes with WireMock

The first mistake is mocking everything. If all tests use mocks, the team may miss real integration failures. Keep real service tests for release confidence.

The second mistake is using loose matchers. A stub that matches any POST to /payments may hide a missing amount or wrong currency.

The third mistake is over-specifying matchers. Matching irrelevant headers, timestamps, or generated IDs creates brittle tests.

The fourth mistake is letting stubs drift. When the real API changes, mocks must change too. Otherwise, tests prove behavior against fiction.

The fifth mistake is ignoring unmatched requests. Unexpected calls should be visible.

The sixth mistake is not resetting state. Stateful scenarios can leak between tests and cause random failures.

The seventh mistake is putting sensitive production data into recorded stubs. Scrub recordings before committing.

The eighth mistake is treating WireMock as a performance tool. It can simulate delay, but it does not replace proper load testing of real systems.

Practical Testing Patterns

Use WireMock for client behavior tests:

  • Service returns 200 with expected data.
  • Service returns 404 for missing dependency data.
  • Service returns 429 and app retries after backoff.
  • Service times out and app shows fallback.
  • Service returns malformed JSON and app handles error.
  • Service returns partial data and app shows degraded state.

Use real API tests for:

  • Authentication between systems.
  • Actual provider response schema.
  • Database persistence.
  • Environment routing.
  • Release candidate confidence.
  • End-to-end business workflows.

This split keeps the suite fast and honest.

Maintaining WireMock Stubs Over Time

WireMock starts as a productivity tool and becomes a risk if nobody owns it. Every stub is a small version of a provider contract. When the provider changes, the stub may need to change too. Assign ownership for mappings just as you assign ownership for automated tests.

Add stub review to API change review. If a provider adds a required field, changes an enum, removes a response field, or changes an error code, update affected stubs in the same change cycle. If a consumer test depends on the old behavior, decide whether the consumer must adapt or the provider has introduced a breaking change.

Keep stubs organized by provider and behavior:

wiremock/
  payments/
    payment-authorized.json
    payment-declined.json
    payment-timeout.json
  inventory/
    sku-available.json
    sku-out-of-stock.json
  customers/
    customer-active.json
    customer-not-found.json

Names matter. A file called response1.json tells reviewers nothing. A file called payment-declined-insufficient-funds.json explains the scenario.

Add a small smoke check that starts WireMock and verifies core stubs load successfully. This catches broken JSON, invalid mappings, and missing body files before the application tests fail in a confusing way.

WireMock Test Case Template

Use this template when adding a new mock scenario:

FieldWhat to Define
DependencyExternal service being mocked
ScenarioBusiness condition represented by the stub
Request MatchMethod, path, query, headers, and body fields that matter
ResponseStatus, headers, body, delay, or fault
Consumer BehaviorWhat the system under test should do
Contract SourceOpenAPI, provider docs, captured response, or team agreement
Real Integration CoverageWhere this behavior is tested against the real provider

This template forces an important question: why is this mock safe to trust? If the answer is unclear, the stub may need a real integration test or contract check beside it.

Example WireMock Review Questions

Before approving a new WireMock stub, ask a few direct questions. Does this stub represent a behavior the real provider supports? Is the request matcher strict enough to catch wrong client behavior? Is it loose enough to ignore irrelevant headers and timestamps? Does the response body include the fields the application actually reads? Is there a matching negative or failure scenario? Is the same behavior also covered somewhere against the real provider?

These questions keep mocks useful. Without review, teams often accumulate happy-path stubs that make tests pass but do not protect production behavior. A good mock suite should include success, validation error, authorization error, not found, timeout, and retry-triggering examples for the highest-risk dependencies.

Also decide how stale stubs will be found. Some teams compare mock responses against provider examples during CI. Others review stubs during API change meetings. Smaller teams may schedule a monthly stub audit. The mechanism matters less than the habit: mocks need maintenance, not only creation.

Where QABattle Fits in Your Practice

Mocking requires judgment. You must decide what behavior to simulate, what to match, what to assert, and what still needs real integration. Those decisions are a QA skill, not just a tool command.

Use QABattle testing battles to practice this thinking. For any API scenario, ask: what dependency behavior is hard to reproduce, what can be mocked safely, what must be verified against the real provider, and how would a mock drift create risk?

API mocking with WireMock is powerful when used with discipline. It gives you control over dependency behavior, failure modes, and local testing. Keep stubs aligned with real contracts, combine mocks with real integration checks, and your API test strategy becomes faster without becoming naive.

FAQ

Questions testers ask

What is WireMock used for?

WireMock is used to mock HTTP APIs for testing. Teams create stubs that return controlled responses so applications can be tested when dependencies are unavailable, unstable, expensive, slow, or difficult to place into specific error states.

Is WireMock only for Java?

WireMock is written in Java and works very well in Java tests, but it can also run as a standalone server or Docker container. Any application or test framework that can call HTTP endpoints can use WireMock as a mock API server.

What is the difference between mocking and contract testing?

Mocking simulates a dependency so tests can run in a controlled way. Contract testing verifies that consumers and providers agree on request and response expectations. WireMock can support contract workflows, but mocks must be kept aligned with real provider behavior.

Can WireMock simulate slow or failing APIs?

Yes. WireMock can add fixed delays, random delays, connection faults, malformed responses, specific error status codes, and scenario-based state changes. This makes it useful for timeout, retry, fallback, and resilience testing.

Should every API test use WireMock?

No. Use WireMock when isolation and control are valuable. Still run tests against real services for integration confidence, environment validation, authentication, persistence, and release readiness. Mock tests and real integration tests answer different questions.