Speed Up Backend Development With WireMock And Docker

George Berar
7 min readNov 7, 2022

In today’s article I will talk about a simple strategy we can use to speed up the development of a backend service when we have to deal with external APIs.

Image credits

Before we start you can find the entire code here. Ok, let’s dive in!

Background

How important is the local environment configuration of a backend service? Slightly important? Very important? Not important at all?

I don’t think there’s an accurate answer and everyone has a different opinion but I would say it’s a critical piece especially in the early stages of development.

First of all, the local environment is the place where the actual development process is happening. Each feature is implemented, tested and debugged before deploying it to a dedicated environment.

Second of all, as a new joiner, the local environment is the first thing you bump into. Before developing, testing and debugging you need to have a working local environment.

So, what has the local environment configuration to do with development speed?

Investing little time to properly configure it or using the wrong strategy from beginning could slow down the development on the long run and could cost your team time and resources.

The Problem

Most of the backend services we develop nowadays have at least one interaction with other service(s), whether we speak about REST API calls, gRPC calls or queues. This creates dependencies that we have to manage during development lifecycle and we have to use the correct strategy from the early stages to make our lives easier and our backend more manageable. My focus for this article will be around REST API calls.

I’ve seen so many cases in my past projects when a release was delayed because a feature involving a REST API integration between two backends could not be properly tested. The main reason in most of them was the local environment configuration which was incomplete and very unreliable to use for testing and debugging so the developers did some ‘local hacks’ in order to finish the implementation and performed the real test after deploying the changes to our dedicated environments. It was not a surprise to find out that it doesn’t work and behave the way it should and it was a lot of back and forth to implement the fix and deploy again hoping it could solve the issue. It did but at what cost? The testing team delayed their work with 1 week, the trust from the business decreased because they had to delay the release, other depending features were blocked, developer’s motivation went down the drain, a complete refactoring was needed in some cases and the list can go on.

I’ve seen extreme cases as well, when someone struggled with the local environment and decided it was faster and easier to use the pre-production URL of a backend for debugging purposes in order to fix an issue. What do you think it happened? The records from that backend’s database were corrupted or even deleted and had a direct impact on that team.

We, as developers, will always find a way to ‘hack’ unreliable local environments in order to do our jobs and implement that feature. Sometimes it pays off but it’s only delaying the inevitable: investing time in refactoring and proper configuration.

So, how can we manage these external APIs the right way in our local environment configuration?

The Solution

What if we could somehow mimic the other services and have them running in our local environment? What if we could somehow have control over their APIs and configure different requests/responses we want to test out?

Well, no more ‘what if’ because we can use WireMock .

WireMock is basically an HTTP server for building mock APIs and the nice thing about it is that we can configure different kind of request/response templates and matchers based on our needs.

As an example let’s consider our backend service needs to fetch TODOs from an external service and the API contract of those endpoints look like this:

1.Get Todos

GET /api/v1/todos
Response

2.Get Todo by Id

GET /api/v1/todos/{id}
Response

Host of the TODO service was omitted for simplicity.

Step 1. Docker

Because we want to run this TODO service on demand and as simple as possible with minimum configuration we’ll use Docker.

Luckily for us WireMock provides a Docker image we can already customise and run.

Create a new file docker-compose.yml in your preferred folder location with the following content:

There are two important things you need to pay attention to:

  • volumes — mount a local folder called ‘local-setup’ to a dedicated folder used by WireMock
  • ports — map 8383 port to the internal 8080

More details about Docker Compose can be found here.

Step 2. WireMock

In our docker-compose.yml we specified that we want to mount a local folder called local-setup to a dedicated folder used by WireMock, right?

The reason is because in this local folder we’ll define the request/response templates as JSON files and we want them to be automatically imported and discoverable by WireMock.

Create a new folder called local-setup and inside of it create another folder called mappings like below:

Folder structure

The ‘__files’ folder is not mandatory but it will be automatically created by WireMock when we run the Docker container at the end.

After we created the expected folder structure let’s mock the first endpoint from TODO service.

To do so, create a new JSON file get-todos.json under mappings folder:

Mocking GET /api/v1/todos

As you can see in the request property we specified the HTTP method and the resource path and in the response property we defined how the response looks like (HTTP status, headers and body).

You can find more on configuring the request/response templates here.

In order to mock the second endpoint we need to create a new JSON file get-todo.json under same folder:

Mocking GET /api/v1/todos/{id}

The file content is exactly the same except for line 4 where we need to define the correct resource path.

Step 3. Run & Test

The final step is to run our TODO service and check if the mocked endpoints work properly.

Run

Run docker-compose up command in a terminal in the same folder where you defined the docker-compose.yml file. You should see something like this:

If you are using Docker for Desktop you should be able to see the running container under Containers:

Another alternative to see if the container runs is to use docker ps command:

Test

In order to test our mocked endpoints we need to use http://localhost:8383 as host (remember the ports definition in docker-compose file) and add the target resource path.

Opening http://localhost:8383/api/v1/todos in browser gives us the expected response:

The same applies for the second endpoint:

Error Cases

Our mocked endpoints work as expected for the happy flows but how can we configure this TODO service for some error cases, like a TODO not found based on the given id?

We need to create a new file get-todo-not-found.json under mappings folder having the following content:

What we want to achieve is to get our TODO details whenever we query the http://localhost:8383/api/v1/todos/d8b25784-c16f-449a-9006-6972e8a9111b and get 404 Not Found in all the other cases when we use a different UUID.

Because the resource path is similar in both cases we want WireMock to match the successful path first and then the rest. To do so we need to use an extra property called priority in order to inform WireMock how to prioritise the matching. As you can guess the same property will be added in the get-todo.json file having value set to 1.

You can read more about Stub Priority here.

Trying our endpoint with a different UUID gives us the expected response:

That’s it!

WireMock is a powerful tool we can leverage in our local environment configuration to make our backend more manageable and the development process less painful. Used together with Docker we can configure, mimic and spin up external APIs in minutes and this gives us a huge advantage when time is our enemy.

Conclusion

As always please keep in mind this approach might or might not suit your project context or needs and I’m not in the position to say there’s no other way to do it differently or better. I really hope you enjoyed it and had fun reading it.

Stay safe and remember you can find the code here.

--

--

George Berar

Senior Software Engineer • Freelancer • Tech Enthusiast