Building a Stub Server with WireMock and Docker
A practical approach to API stubbing and virtualization
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.