Building a Stub Server with WireMock and Docker

A practical approach to API stubbing and virtualization

Jonathan Manera
4 min readFeb 15, 2023
Images from Wiremock and Icons8

In the world of microservices, we as developers need to deal with the complexity of developing services across distributed systems. These microservices are isolated from each other, which has many advantages but also some drawbacks, such as the higher complexity in testing.

This complexity in testing can be overcome by creating an isolated test environment, which means having an external world that always works exactly as expected.

In this article, we are going to explore how to emulate the “external world” of a microservice by creating a Stub Server using WireMock and Docker.

Stubbing the Users API

WireMock supports rich definitions for matching requests and mocking the responses. In the following examples, we will create a stub for the Users API configuring the WireMock server via the JSON API over JSON files.

Basic Stubbing

To show how the JSON API works, suppose we want to match method and url in the request and return a status code in the response. We can create the described stub in a file with a .json extension as follows.

{
"request": {
"method": "POST",
"urlPathPattern": "/api/users"
},
"response": {
"status": 201
}
}

Here, when the request matches the POST method and the URI /api/users, the stub always responds with status 201 regardless of the body sent.

URL Matching

The request.urlPathPattern property allows URLs to be matched, whether by equality (as in the example above) or by regular expression.

URL matching is often useful to match path parameters, as in the code example below.

{
"request": {
"method": "DELETE",
"urlPathPattern": "/api/users/([0-9]*)"
},
"response": {
"status": 204
}
}

Mocking the Response Body

To mock a response body, we can use JSON files. Define the response.bodyFileName property, and the server will read the content from the files stored in the path /home/wiremock/__files.

{
"request": {
"method": "GET",
"urlPathPattern": "/api/users/([0-9]*)"
},
"response": {
"status": 200,
"bodyFileName": "findUserById_res.json",
"headers": {
"Content-Type": "application/json; charset=utf-8",
"Server": "wiremock"
}
}
}

Here, the response.bodyFileName property has the file path relative to __files.

Additionally, you can send headers in the response by specifying the header key/value within the response.headers property.

Query Params Matching

The request.queryParameters property allows specifying the query parameters that match a regular expression defined in the matches property.

{
"request": {
"method": "GET",
"urlPathPattern": "/api/users",
"queryParameters": {
"page": {
"matches": "\\d+"
}
}
},
"response": {
"status": 200,
"bodyFileName": "findUsers_res.json",
"headers": {
"Content-Type": "application/json;charset=UTF-8",
"Server": "wiremock"
}
}
}

Body Matching

Using the request.bodyPatterns.matchesJsonPath property, you can check if a JSON field is valid as per a JSON Path expression.

{
"request": {
"method": "PUT",
"urlPathPattern": "/api/users/([0-9]*)",
"bodyPatterns": [
{
"matchesJsonPath": "$.first_name"
},
{
"matchesJsonPath": "$.last_name"
},
{
"matchesJsonPath": "$.birthday"
}
]
},
"response": {
"status": 204
}
}

You can also match the equality of the body using the request.bodyPatterns.equalToJson property, and defining the JSON with string literal.

{
"request": {
...
"bodyPatterns": [
{
"equalToJson": "{\"first_name\":\"John\",\"last_name\":\"Doe\",\"birthday\":\"1989-04-04\"}"
}
]
},
...
}

Running the Server in Docker

In this tutorial, we will create a Docker image based on the latest official Wiremock Docker Image.

The Project Directory Structure

Your project directory structure should look like the following folder tree.

.
├── Dockerfile
├── __files
│ ├── findUserById_res.json
│ └── findUsers_res.json
└── mappings
├── createUser.json
├── deleteUser.json
├── findUserById.json
├── findUsers.json
└── modifyUser.json

We split the JSON files into two directories. As mentioned earlier, the responses are stored in the __files directory, while the configuration files are stored in the mappings directory.

Creating the Dockerfile

The Dockerfile file is used to create a container image. This file should be located in the root directory of the project.

FROM wiremock/wiremock:latest-alpine

COPY mappings /home/wiremock/mappings
COPY __files /home/wiremock/__files

EXPOSE 8080

CMD ["java", "-cp", "/var/wiremock/lib/*:/var/wiremock/extensions/*", "com.github.tomakehurst.wiremock.standalone.WireMockServerRunner", "--global-response-templating", "--container-threads=20", "--async-response-enabled=true", "--async-response-threads=20", "--no-request-journal=true", "--disable-gzip=true"]

The FROM keyword requires a Docker container image as base image.

The COPY keyword makes a copy of the directories with the JSON files from the project into the absolute paths /home/wiremock/__files and /home/wiremock/mappings respectively.

The EXPOSE keyword exposes the port 8080 on the docker virtual network.

The CMD keyword includes the commands to run when the container is launched.

Creating the Docker Image

Generate a Docker image using the command below.

docker image build --tag <your-docker-user>/usersapi-mocks .

Consider publishing the generated image into DockerHub.

docker image push <your-docker-user>/usersapi-mocks

Creating the Docker Container

Now that you have an image containing the stubs, create a Docker container in which the server will run using the following command.

docker container run -it --rm -p 8080:8080 \
--name usersapi-mocks <your-docker-user>/usersapi-mocks

Next Steps

Once the Stub Server is ready you can deploy it in your test environments or take advantage of tools such as Testcontainers to create automated tests.

Thanks for reading. I hope this was helpful!

The example code is available on GitHub.

--

--

Jonathan Manera

If you wish to make a Java app from scratch, you must first invent the universe.