Sitemap
Javarevisited

A humble place to learn Java and Programming better.

WireMock Advanced Usage Patterns

4 min readAug 20, 2025

--

Press enter or click to view image in full size
Photo by Sigmund on Unsplash

This post explores some approaches I have found useful when using WireMock. Some of these suggestions depend on the type of test. To avoid ambiguity, I’m using the test categorisation described in the Microservice Testing Strategy.

Use Shortcut Methods for Stubbing

Not good:

stubFor(
get("/pricing-service/pricing")
.willReturn(aResponse()
.withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.withBody(response)
)
);

Good:

stubFor(
get("/pricing-service/pricing")
.willReturn(okJson(response))
);

URL Path Templates (New in WireMock 3.0.0)

WireMock now supports URL path templates like /customers/{customerId}/orders/{orderId}. This feature simplifies creating flexible, path-based stubs:

get(urlPathTemplate("/customers/{customerId}/orders/{orderId}"))
.withPathParam("customerId", equalTo(customerId))
.withPathParam("orderId", equalTo(orderId))

This makes it easier to verify dynamic path parameters. You can also choose not to verify a specific path parameter. In that case, get(urlPathTemplate("/customers/{customerId}/orders/{orderId}")) will match any request that conforms to the template, regardless of the actual values for customerId or orderId.

Check more about this feature from WireMock official documentation.

Verifying Requests

WireMock supports both strict and lenient verification strategies.

  • Strict stubbing: when stubbing, match full request, including request body and any path/query parameters.
  • Lenient stubbing + explicit verification: when stubbing, match only request path template.

The second option makes debugging easier — there’s no need to sift through logs to diagnose a mismatched request. However, it does require you to add an extra step to verify expectations that a strict stub might catch automatically. Check more about the verification API.

I prefer using strict stubbing for integration tests. For component-level acceptance tests, lenient stubbing and separate verification is usually a better fit.

Integration Tests — Selectively Wrap Wiremock Methods

To remove duplication and simplify test code in integration tests, I prefer to extract a new method that contains part of the request stubbing or verification logic.

For example, I can extract a method for customer creation request verification:

private static RequestPatternBuilder createCustomer(UUID customerId) {
return putRequestedFor(urlPathEqualTo("/v1/customers"))
.withHeader("X-Session-Context", equalTo(TOKEN)));
}

Then use it inside the test like: verify(createCustomer(CUSTOMER_ID).withRequestBody(expectedCustomer));

Get Ürgo Ringo’s stories in your inbox

Join Medium for free to get updates from this writer.

This way, I can choose the level of detail I want to expose to my test while still taking advantage of WireMock’s fluent API.

Component-level Acceptance Tests — Wrap the Whole Stub in a Thin Abstraction

Creating a few factory methods as suggested above is a good approach for integration tests. However, for component-level acceptance tests, I want to express intent and have no implementation information leak into the test code. Hiding technical details helps to keep the focus on the behaviour rather than the state setup mechanics.

@Test
void applyCustomerDiscount() {
customerService.havingCustomer(CUSTOMER_ID);
//the rest of test code...
...
}

Use Request Builders

If test cares about just a few values in the request, I can pass these values as parameters to the stub like: policyService.createPolicy(policyNumber, policyStartDate).

However, if the test cares about more values, then it’s better to pass a request object to the stub. One option is to reuse the request DTOs used by production code. I’m not a big fan of this solution as it requires me to specify every value (even the ones that are not relevant in the given test context) or use the Builder pattern for constructing the request object (I find this to be a misuse of Builder).

A better alternative is creating a dedicated test request builder with default values for all fields. This way, I can explicitly set only the interesting values from the test setup block and leave defaults for all other required fields.

policyService.verifyPolicyCreated(aPolicy()
.withPeriod(12)
.withCountry("CZ")
.withInsuredObject(
anInsuredObject()
.withSerialNo(assetIdentifier)
.withManufacturer("Apple")
.withModel("MacBook Pro (14-inch, 2024")
.withType("laptop")
.withInsuredValue("820.00")));

Using Lombok’s @With and test data generator like Instancio reduce the boilerplate quite a bit, so in most cases I find this kind of test Builder well worth the effort.

Use Scenarios for Complex Interactions

Sometimes a test involves multiple sequential calls to a stubbed service (either several calls to the same endpoint that should return different responses each time or just calling multiple different endpoints).

For example, an insurance policy provider API requires the following interaction:

  • first send a policy creation request,
  • then poll another “queue” endpoint until the policy is ready.

One option to set up such a stub is the following:

policyService
.expectCreatePolicy()
.andAddPolicyToQueue(policyId, policyNumber);

Again, I would be okay with exposing all this to the test body in an integration test. For a component acceptance test, I prefer hiding this information like:

policyService.createPolicySuccessfully(policyId, policyNumber);

With this approach, I can internally use WireMock scenarios when needed without changing a single line in the test code.

Here is how the createPolicySuccessfully method implementation looks like:

public void startCreatePolicySuccessfully(String policyId, String policyNumber) {
var queueId = newQueueId();
//stub create policy request
stubFor(post(path("/policy"))
.willReturn(okJson("{}")
.withHeader("Location", locationHeaderValue(queueId))));

//1st time queue is accessed, it returns policy as pending
stubFor(getQueueRequest(queueId)
.inScenario("Create Policy Successfully")
.whenScenarioStateIs(STARTED)
.willSetStateTo("Policy Pending")
.willReturn(okJson(pendingPolicyBody())));

//2nd time queue is accessed, it returns policy created
stubFor(getQueueRequest(queueId)
.inScenario("Create Policy Successfully")
.whenScenarioStateIs("Policy Pending")
.willSetStateTo("Policy Created")
.willReturn(okJson(successfulPolicyBody(policyId, policyNumber))));
}

Handling multiple similar scenarios and code duplication:

If I need to have multiple “scenarios” where the same endpoints are stubbed there will be some copy-paste. I think this is fine. I do not try to eradicate every single duplication from test code.

Looking at it from another angle, moving all similar code into some kind of factory method like stubGetPolicyFromQueue(queueId, scenario, startState, endState) would avoid some duplication but also make the code less clear. WireMock DSL is already quite succinct — hiding its fluent Builder API behind a Factory with X parameters is not a good tradeoff.

--

--