In this article we will walk through the main Java concepts behind WireMock: how you configure the server, choose a port, describe requests and responses, and how everything ends up as a StubMapping. The goal is that you not only know how to use the API, but also why it is structured this way, the way an experienced engineer would reason about test doubles.
Configuring WireMockServer with WireMockConfiguration
WireMockConfiguration is the object that describes how your WireMock HTTP server should run. You rarely construct it directly; instead you use a static factory called options(), which returns a configuration builder.
At a high level:
WireMockConfigurationcontrols ports, HTTPS, file locations, extensions, and more.- The fluent style (via
options()) encourages explicit, readable configuration instead of magic defaults scattered through the codebase. - Because it is a separate object from
WireMockServer, you can reuse or tweak configuration for different test scenarios.
Example shape (without imports for now):
WireMockServer wireMockServer = new WireMockServer(
options()
.dynamicPort()
.dynamicHttpsPort()
);
You pass the built configuration into the WireMockServer constructor, which then uses it to bind sockets, set up handlers, and so on. Conceptually, think of WireMockConfiguration as the blueprint; WireMockServer is the running building.
Dynamic Port: Why and How
In test environments, hard‑coding ports (e.g. 8080, 9090) is a common source of flakiness. If two tests (or two services) try to use the same port, one will fail with “address already in use.”
WireMock addresses this with dynamicPort():
dynamicPort()tells WireMock to pick any free TCP port available on the machine.- After the server starts, you ask the server which port it actually bound to, via
wireMockServer.port(). - This pattern is ideal for parallel test runs and CI environments, where port availability is unpredictable.
Example pattern:
WireMockServer wireMockServer = new WireMockServer(
options().dynamicPort()
);
wireMockServer.start();
int port = wireMockServer.port(); // the chosen port at runtime
String baseUrl = "http://localhost:" + port;
You then configure your HTTP client (or the service under test) to call baseUrl, not a hard‑coded port. The rationale is to shift from “global fixed port” to “locally discovered port,” which removes an entire class of brittle test failures.
Creating a Stub: The Big Picture
When we say “create a stub” in WireMock, we mean:
Define a mapping from a request description to a response description, and register it with the server so that runtime HTTP calls are intercepted according to that mapping.
This mapping is built in three conceptual layers:
- A request pattern (what should be matched).
- A response definition (what should be returned).
- A stub mapping that joins these two together and gives it identity and lifecycle inside WireMock.
In Java, the fluent DSL exposes this as:
wireMockServer.stubFor(
get(urlEqualTo("/api/message"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/plain")
.withBody("hello-wiremock")
)
);
This one line of code hides several objects: a MappingBuilder, a RequestPattern, a ResponseDefinition, and eventually a StubMapping. The design encourages a declarative style: you describe what should happen, not how to dispatch it.
MappingBuilder: Fluent Construction of a Stub
MappingBuilder is the central builder used by the Java DSL. Calls like get(urlEqualTo("/foo")) or post(urlPathMatching("/orders/.*")) return a MappingBuilder instance.
It is responsible for:
- Capturing the HTTP method (GET, POST, etc.).
- Associating a URL matcher (exact equality, regex, path, etc.).
- Enriching with conditions on headers, query parameters, cookies, and body content.
- Attaching a response definition via
willReturn.
You rarely instantiate MappingBuilder yourself. Instead you use static helpers from the DSL:
get(urlEqualTo("/api/message"))
post(urlPathEqualTo("/orders"))
put(urlMatching("/v1/users/[0-9]+"))
Each of these returns a MappingBuilder, and you chain further methods to refine the match. The rationale is to keep your test code highly readable while still configuring quite a lot of matching logic.
RequestPattern: Describing the Request Shape
Under the hood, MappingBuilder gradually accumulates a RequestPattern (or more precisely, builds a RequestPatternBuilder). A RequestPattern is an object representation of “what an incoming HTTP request must look like for this stub to apply.”
A RequestPattern may include:
- HTTP method (e.g. GET).
- URL matcher:
urlEqualTo,urlPathEqualTo, regex matchers, etc. - Optional header conditions:
withHeader("X-Env", equalTo("test")). - Optional query param or cookie matchers.
- Optional body matchers: raw equality, regex, JSONPath, XPath, and so on.
Example via DSL:
post(urlPathEqualTo("/orders"))
.withHeader("X-Tenant", equalTo("test"))
.withQueryParam("source", equalTo("mobile"))
.withRequestBody(matchingJsonPath("$.items[0].id"));
Each of these DSL calls contributes to the underlying RequestPattern. The motivation for this design is to let you express complex request matching without writing imperative “if header equals X and URL contains Y” code; WireMock handles that logic internally.
ResponseDefinition and aResponse: Describing the Response
If RequestPattern says “what we expect to receive,” then ResponseDefinition says “what we will send back.” It captures all aspects of the stubbed response:
- Status code and optional status message.
- Headers (e.g., content type, custom headers).
- Body content (string, JSON, binary, templated content).
- Optional behaviour like artificial delays or faults.
The idiomatic way to construct a ResponseDefinition in Java is via the aResponse() factory, which returns a ResponseDefinitionBuilder:
aResponse()
.withStatus(201)
.withHeader("Content-Type", "application/json")
.withBody("{\"id\":123}");
Using a builder for responses has several benefits:
- It separates pure data (status, headers, body) from the network I/O, so you can reason about responses as values.
- It encourages small, focused stubs rather than ad‑hoc code that manipulates sockets or streams.
- It allows extensions and transformers to hook into a well‑defined structure.
Once built, this response definition is attached to a mapping via willReturn.
willReturn: Connecting Request and Response
The willReturn method lives on MappingBuilder and takes a ResponseDefinitionBuilder (typically produced by aResponse()).
Conceptually:
- Before
willReturn, you are only describing the request side. - After
willReturn, you have a complete “if request matches X, then respond with Y” mapping. - The resulting
MappingBuildercan be passed tostubFor, which finally registers it with the server.
Example:
get(urlEqualTo("/api/message"))
.willReturn(
aResponse()
.withStatus(200)
.withBody("hello-wiremock")
);
The wording is deliberate. The DSL reads like: “GET /api/message willReturn this response.” This is a very intentional choice to make tests self‑documenting and easy to skim.
StubMapping: The Persisted Stub Definition
Once you call stubFor(mappingBuilder), WireMock converts the builder into a concrete StubMapping instance. This is the in‑memory (and optionally JSON‑on‑disk) representation of your stub.
A StubMapping includes:
- The
RequestPattern(what to match). - The
ResponseDefinition(what to send). - Metadata: UUID, name, priority, scenario state, and other advanced properties.
StubMapping is what WireMock uses at runtime to:
- Evaluate incoming requests against all known stubs.
- Decide which stub wins (based on priority rules).
- Produce the actual HTTP response that the client receives.
From an architectural perspective, StubMapping lets WireMock treat stubs as data. That is why you can:
- Export stubs as JSON.
- Import them via admin endpoints.
- Manipulate them dynamically without recompiling or restarting your tests.
WireMock Class: The Fluent DSL Entry Point
The WireMock class is the static gateway to the Java DSL. It provides methods used throughout examples:
- Request builders:
get(),post(),put(),delete(),any(). - URL matchers:
urlEqualTo(),urlPathEqualTo(), regex variants. - Response builders:
aResponse(), plus convenience methods likeok(),badRequest(), etc. - Utility methods to bind the static DSL to a specific server (
configureFor(host, port)).
In tests you typically import its static methods:
import static com.github.tomakehurst.wiremock.client.WireMock.*;
This is what enables code such as:
get(urlEqualTo("/api/message"))
.willReturn(aResponse().withStatus(200));
instead of more verbose, object‑oriented calls. The goal is to minimize ceremony and make test intent immediately obvious.
A Simple Example
Let’s now put all these pieces together in a small JUnit 5 test using:
- Java 11+
HttpClient. WireMockServerwithdynamicPort().- A single stub built with the core DSL concepts we have discussed.
This example intentionally avoids any build or dependency configuration, focusing only on the Java code.
import com.github.tomakehurst.wiremock.WireMockServer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static org.junit.jupiter.api.Assertions.assertEquals;
class WireMockExampleTest {
private WireMockServer wireMockServer;
@BeforeEach
void startServer() {
// Configure WireMock with a dynamic port to avoid clashes.
wireMockServer = new WireMockServer(
options().dynamicPort()
);
wireMockServer.start();
// Bind the static DSL to this server instance.
configureFor("localhost", wireMockServer.port());
}
@AfterEach
void stopServer() {
wireMockServer.stop();
}
@Test
void shouldReturnStubbedMessage() throws Exception {
// Create a stub (MappingBuilder -> RequestPattern + ResponseDefinition)
wireMockServer.stubFor(
get(urlEqualTo("/api/message"))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/plain")
.withBody("hello-wiremock")
)
);
// Build an HTTP client and request using the dynamic port.
HttpResponse<String> response;
try (HttpClient client = HttpClient.newHttpClient()) {
String baseUrl = "http://localhost:" + wireMockServer.port();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/api/message"))
.GET()
.build();
response = client.send(request, HttpResponse.BodyHandlers.ofString());
}
// Validate that the stub mapping was applied correctly.
assertEquals(200, response.statusCode());
assertEquals("hello-wiremock", response.body());
}
}
How to validate this example
To validate the example:
- Ensure you have WireMock and JUnit 5 in your project dependencies (via Maven, Gradle, or your build tool of choice).
- Run the test class.
- The test passes if:
- The
WireMockServerstarts on a dynamic port without conflicts. - The request to
/api/messageis matched by theRequestPatterndefined in theMappingBuilder. - The
ResponseDefinitioncreated withaResponse()and attached viawillReturnproduces the expected status and body.
- The
Leave a Reply