Integration testing with Wiremock

Kubilay Örcün
Trendyol Tech
Published in
8 min readMay 11, 2021

--

Hi, it’s Kubilay from Customer Services team at Trendyol. As a team we develop and maintain different projects. To be able to safely rely on our projects and to increase our delivery focus we always try to improve our operations.

Integration testing is one of these operational steps and lately we had the chance to focus on our integration tests and things have changed for the better. I will try to explain these operations related to the integration testing processes by answering the following questions:

  • How we were managing the integration tests before Wiremock?
  • What was the problem pushed us to use Wiremock?
  • How do we use Wiremock?

For starters, let’s dive right in with the “Before Wiremock Era”.

Before Wiremock Era

As it can be seen from the image above we have 2 different projects:

  • api-gateway: The main Restful API project.
  • api-gateway-test: Cucumber integration test project using Gherkin and Java.

Back to the CI/CD pipelines, previously the integration test project would run the tests at its own CI pipeline by making requests to the stage instance of the main project. Which can cause several problems, most importantly the environment dependency.

What I mean by environment dependency is that, whenever test project runs a simple scenario like getting an order info, it would make a request to the gateway-api’s stage instance. In return gateway-api project would run implemented business and most probably would make a request to another external api’s stage instance to fetch the requested data. This whole flow can be seen in the image below.

So, what is the problem?

This situation may not seem like a problem at first glance but let me cue you in on what happens after couple of weeks, maybe days. Since this external api is a stage instance, the response for the same request can surprise you with a 404 in the future. Because, maybe the external-api’s owner team were making a DELETE endpoint test and surprise, they deleted the order information we were relying on to run our tests. Simply, that test order is not there anymore. Which means that someone has to find a suitable order information to fix the test. And that order will not be there at some point too. And someone goes to fix tha.. I think you see my point here, this is a vicious cycle and we have a sprint to run.

This is one of the problems at hand and there is one other crucial problem. Let’s say we are 100% sure that requested information will always miraculously be there whenever we feel like making the same request. What about the timeouts and response times? Can you imagine what would it look like if we go to the other teams and asked:

“Hey order team, account team and checkout team. Can you all throttle your stage instances so that we can test out our api’s timeout behavior?”

As any of you can guess this approach is not so feasible. Right at this point, “Wiremock” comes to the rescue.

After Wiremock Era

First of all let’s take look at the bare bones definition of Wiremock’s official website:

WireMock is a simulator for HTTP-based APIs. Some might consider it a service virtualization tool or a mock server.

It enables you to stay productive when an API you depend on doesn’t exist or isn’t complete. It supports testing of edge cases and failure modes that the real API won’t reliably produce. And because it’s fast it can reduce your build time from hours down to minutes.

As soon as you read the definition, you realize this would most probably solve the problems mentioned previously. It could mock all these timeout failure cases and also it could serve mocked responses for those external apis we were talking about.

It means that Wiremock can wait for requests and act as if it is any of our external apis by returning whatever we tell it to return by matching our requests.

In this case we have to make a little tweak to our pipelines as shown in the figure below:

From now on, as seen from the figure above, our integration test project is no longer responsible for running the tests. This step is moved to the gateway-api project’s pipeline. Right at this point the state of the pipelines can be summarized as follows for each project:

  • Gateway-api: Git Push -> Build Project -> Push Built Docker Image to Docker Registry as ‘gateway-api:latest’
  • Gateway-test-api: Git Push -> Build Project -> Push Built Docker Image to Docker Registry as ‘gateway-api-test:latest’

Before we zoom in to the “Integration Test” step, it would be better to take a look at some code snippets which made these steps possible. One of those files would be gitlab-ci.yml file of the gateway-api which is basically a configuration file to manage the CI flow of a project.

Gateway-api’s example gitlab-ci.yml file

Essentially this configuration file makes two things happen in a row:

  • Build and push the latest image to the specified registry.
  • Run the docker-compose file if it exists in the same directory. (We will dive deeper into this flow with examples soon.)

There is one difference with this gitlab-ci file and the one in gateway-api-test project. The integration test project does not run the integration tests hence the “Integration test” stage does not exist in that project’s gitlab-ci file.

To sum up all the things happened until this point:

  • We implemented some kind of a feature in the gateway-api and merged it to the master branch. After this merge, the “:latest” tagged docker image is pushed to the respective registry.
  • We also wrote the integration tests for that feature and also merged it to the master branch which again caused the “:latest” tagged test project’s docker image to be pushed to the respective registry.

If you are still keeping up with what’s happening here comes the famous ‘integration tests’ step.

Zooming in to the “Integration Test” step:

There is only one thing that integration test step does at this point. It runs the

docker-compose up

command because of the “Integration Test” step defined in the gitlab-ci file of the gateway-api. Which will pull the previously pushed gateway-api and gateway-test-api images from the registry as well as the Wiremock image. After pull is completed, it runs all those images with the respective configurations read from the docker-compose.yml file. Which will create the shown docker subnet crawling with different containers communicating with each other. Before continuing with the implementation details of this test steps let’s take a look at the docker-compose.yml file up close.

As you can see, there is not much detail or configuration provided for the wiremock and the gateway-api-test project images to run. The only configuration options passed is the port forwarding options in order to ensure the ports which our running images can communicate over. In a moment, we will also see where these ports will come in handy.

Other than that there is one important configuration options passed as the ACTIVE_PROFILE: “integration”, which ensures that our gateway-api image will be using the configurations specified under the “integration:” profile using the application.yml file. Let’s also take a look at that configuration file too:

We just mentioned that our image is running on “integration” profile. Let’s take a look at what this profile brings us. The host and the port is somewhat expected we can say because the application is running on port 5555 by the profile configuration hence we forward the application’s port to 5555 again in order to ensure the communication between containers.

The caveat here is however the api url configurations. As you can see, the single wiremock instance “http://wiremock:8080” is responsible for all the api’s thanks to the paths given as “/xApiUrl”.

Before giving examples about the test steps as “Given,When,Then” steps of the integration test projects let’s talk a little more about the other profiles.

To start with, we see some regular profiles as “Production” and “Stage” which are — you just guessed it — just plain old production and stage profiles. Other than them, there is one more profile left to mention which is “Dev” profile. As you can guess now, this is related to the wiremock instance. Once you start writing tests using this flow, you will most definitely need to put a breakpoint in the locally running gateway-api project in order the see what is wiremock doing when our project hits these external endpoints. Since our local wiremock instance will be running on “localhost:8081”, by this approach we will be able to debug whether we are able to correctly mock the endpoints or not.

From now on our gateway-api project will hit the endpoints previously mocked by the “@Given” steps of the integration test project through the wiremock api. Which is referred as ‘stubbing’ in the wiremock docs.

Finally, let’s give a simple example what this ‘stubbing’ looks like in action:

Example cucumber feature file scenario
Example cucumber step

As you can see, we ‘register’ a new stub to our wiremock instance through wiremock api. From now on, we can control so many behavior of this particular stub which acts as an endpoint for our gateway api. I linked the documentation not long ago in this post for wiremock stubbing, through that resource now you also can peacefully throttle your external endpoints or at least make them act like they have latency.

But, is it all rainbows and unicorns with this approach? Like any other method, it is not. Because with this approach we are not able follow up with the changes at external endpoints. When a mocked endpoint changes, tests will not care about that change and they will keep giving false positives. This problem can be solved using contract tests to detect if a change occurs at external services’ interfaces.

Up until this point we have covered what was it like before Wiremock and how things are going for the time being. Like any software process there are still drawbacks present and like any ambitious team we are always thriving to overcome them too. I tried to avoid going too deep into implementation details in order not to diverge from the purpose of this article. For future posts we can dive deeper into the implementation of such a system by creating a ‘How to?’ guide with example projects and whatnot.

Till then, take care and use tabs fellow developer.

--

--