How we do automated testing on our backend

davidz profile image David🦉 ・4 min read

After sharing how we do automated testing on our fronted in my previous post I wanted to share too how we test our backend services using our API. You can find the extended version of this post in our blog

API/Integration tests must be faster to develop and run since they do not require any user interface to interact with since they directly hit the endpoints designed in your application.

The platform behind

Our backend platform follows the micro-service pattern, decomposing the application in small services that take care of one isolated piece of the whole business. We follow a hexagonal architecture (aka ports and adapters) to avoid having them highly coupled and facilitating testing activities. To expose all the endpoints we make use of APIs which are consumed by our BFFs using GraphQL.

Regarding the base code, it is written in PHP and it follows the Domain driven design (DDD) pattern. With all this what we have in the end are different bounded contexts (BCs) that completely isolate each side of the business.

The test tool

To test this we opted for Gauge which is an open source tool to write and run tests.

Alt Text

Gauge was created by ThoughtWorks and it is supported by a wide community. It can be used in any platform and it supports many languages for writing your test code. Two key features that we liked are that it’s BDD and it supports data-driven executions, making your tests more escalable.

For writing the tests scenarios themselves Gauge uses specification and concept files. Basically, specification files are those containing the scenarios written in a set of steps and concept files are just a collection of steps that you can reuse in the scenarios.

A spec file

Create Booking Request
Tags: booking

Create booking request and charge (OK)
- - - - - - - - - - - - - - - - - - - - - - 
* Login with "test-user"
* Create a booking lead for listing id "999999" with move in "2021–01–01" and move out "2021–01–31"
* Create a booking request from booking lead
* Charge booking
* Wait until the booking request is converted into booking

This one above is an example of a specification file with one scenario written in Markdown language. The scenario just describes what they do in different steps. Each one of these steps has test code behind, but they can also relate to a concept file since they can be a set of steps themselves.

A concept file

# Charge booking
* JSON from file "./bookings/chargeBookingRequest.json" is set as variable "chargeJson"
* Set path "/payment/booking-requests/{id}/chargeNow" as name "charge" replacing path variable "{id}" with variable "bookingRequest"
* An HttpSampler with name "chargeBR" and path in variable "charge" and type "PUT" and port "xxx" and json "chargeJson"
* Response code is equal to "204"

Here we show an example for the step “Charge booking” that we presented in our spec file. You see all the steps behind that one, meaning that every time we call “Charge booking” from a spec file, all these steps will run.
We usually do that when:

  • There are a lot of small steps required for just one high level task
  • We do not want to show so much logic at the spec file level
  • We are going to reuse such step in many test scenarios

Step implementation

Let’s show as an example the implementation for the step “An HttpSampler with name “chargeBR” and path in variable “charge” and type “PUT” and port “xxx” and JSON “chargeJson””

@Step("An httpSampler with name <name> and path <path> and type <type> and port <port> and json <json>")
public void setHttpSamplerNameTypePortBodyGiven(String name, String path, String type, Integer port, String json) {

// Java code to send an http request with the passed on parameters


Note the @Step annotation. It denotes that the function is a step used in the spec files.


To manage and build the project we use Maven. Maven is an extensively used build automation tool from Apache used in Java projects, although it can also be used for other languages. We’ve been working with it from the very beginning and it’s been useful to us given the big tree of dependencies that we have to maintain, both in-house and external ones.

Since we’ve got all our API tests in the same place, they are all placed in the same repository. So after updating our test collection, be it adding new tests or refactoring, the build is triggered just after pushing the changes to master branch to make sure all is fine. With that, we make available the latest version of the tests for the BCs to grab and run.

Alt Text

Setting up the CI

If you remember from our first post, we use Brigade as our CI system and our BCs use it in the same way. For every BC, we’ve got a Brigade file to indicate the events that must run in such pipeline.
With that, GitItNow (in-house developed tool), makes quite easy to interact with the pipeline and brings visibility to the whole process.

Alt Text

Above there’s one example of a pipeline for one of our BCs.
For the api-tests event it gets the latest successful build from the test repository and runs it.

Worth mentioning too that we have enabled parallelization in our Ci runs to be able to run them faster and react soon in case anything goes wrong.


With all commented here, we ended up with a robust framework that enables us to track the quality of the backend code we deploy and react soon in case a red flag shows up. Is it perfect? Nope (I'd say nothing is). We still deal with collateral issues such as stuck queues or false positives due to misleading data or other kind of human errors. But there are also some other aspects such as the escalability that a BDD approach gives us, getting to a point where there are so many steps developed that we almost don't need to develop more but use what exists.

Hope you enjoyed the post 🚀

Posted on Mar 26 by:

davidz profile



QA automation engineer


markdown guide

I never heard of Gauge before. Thanks for giving a brief overview of it.