undo
Go Beyond the Code
arrow_forward_ios

Implementing Stress Tests with Gatling

November 7, 2023

Introduction

Stress tests allow engineering teams to simulate real world scenarios that might be faced on production applications to ensure that they are prepared for the traffic and load expected. More concretely, stress tests allow you to execute a parameterized set of concurrent users access executing specific requests at a designed cadence, trying to replicate those that are done in  use cases such as a user logging in, registering in an application, creating a new data item, etc. 

At Ensolvers, we use several tools for stress testing, Gatling being one of the most prominent. Some of the advantages of Gatling are that: 

  1. Tests can be implemented 100% in Java via a custom DSL
  2. Common functions like JSON parsing, loading sample data from CSVs among others are provided out of the box
  3. Tests can be run via a Maven goal, so they can easily run both locally and remotely just by having a JDK installed
  4. It creates visual reports that can be rendered on any browser, including useful stats like response time grouped per request per minute, dynamic graphs showing error rate in different moments of test execution, among others.

Gatling by example

In this section we are going to show some real world examples of how to define a Gatling test, taken from one of our projects. We are going to start with pieces of code representing particular tests, and then at the end we are going to show the final test composing all these building blocks. For this example, we are going to assume that we are going to stress test a Project Management app, thus we are going to have a REST API that allows us to create, update and delete Projects.

Let's start with some public endpoints that we expect to handle a lot of traffic. First, we will start with a status endpoint that is used to check if the particular node is "healthy" from the load balancing perspective. We can use the http builder function for that purpose.

In this case, we've created the class HttpProtocolConfig that contains all the configuration needed to use http with these particular endpoints, in this case, the headers.

Then, we check if the status was the expected (200) using Gatling checks and we customize the name of it, to have better clarity on the reports: 

The result of this function is a ChainBuilder, which is the minimal building block that Gatling offers for expressing individual test actions that can be combined or chained.

With this simulation ready and working, we are now going to create a login simulation that saves the user token on the Gatling session. To do this, we needed the user login data to send to the endpoint with a POST request. For that purpose, we've used Gatling feeders.

As you can see, this method receives an email and password, and builds a JSON string to use it on the body of the post request. We save the token using jsonPath, a check type given by Gatling to extract data of the response passing a String formatted with JSONPath. That value is stored in the Gatling session, which is a virtual user’s state that functions as a dictionary, with the key stored in the constant SessionKeys.USER_TOKEN so it can be used in the future parts of the test.

With the post building piece ready, we compose it with a Gatling feeder which obtains the user from an external source. We make use of the Gatling expression language to extract them.

After having this ready, we can now test endpoints that require auth, since with postLoginRequest we can now obtain valid auth tokens which are saved into the Gatling session.


Making an authenticated PUT request

Now let's show how we solve doing PUT (update) requests to update a Project object in the application. In this case, we needed the id of the item to be updated and the payload with the data that will be changed.

The method that will test the request receives all the data needed to generate the body to send, adds the id to the url path, and configures the headers calling the function HttpProtocolConfig.getHeadersWithToken(token). That function receives the token stored in the session and transforms it to a map in which the Authorization header is included to authenticate the requests.

In this test case, we use a previously created Project already saved in the session as a string, parse it to a JSON object, and obtain the id from it to send to the request builder.

To modify the Gatling session we need to return a new Session object since they are immutable by definition. That means that any session.set() method that you execute, will return a new session with the updated (key, value) pair.

To generate a random name to update this project, we use Gatling built-in functions, specifically “#randomAlphanumeric(n)”, being “n” the number of characters wanted.

Making an authenticated DELETE request

Following the previous example, to do a DELETE request we only need the id of the project we want to remove. The request builder method receives the id, adds it to the url path and generates the http request adding the token to the headers.

To obtain the id we access the Gatling session, parse the saved project to a JSON object, and then we get the id to set it with a new key in the Session. Then, we execute the delete request, and chain it to another session call to also delete the project from the session. To reduce the verbosity of the example, we build the request in the ProjectRequests.deleteProjectRequest() method.

Putting it all together

With our requests and chains created, we only miss a Gatling Scenario to coordinate all these steps in sequence. In this example, we used the pause method that generates a delay between the requests to simulate a user clicking or writing to make these requests. We also used the repeat method to allow us to change the amount of Projects we want the CRUD to manage, and the exitHereIfFailed method, that will capture any error, and terminate the execution. Note that we are using all the individual testing blocks we defined before: loginAdminUser, updateProjectFromSession and deleteCreatedProject

In this example we make use of other methods that will not be detailed here for the sake of simplicity:

  1. createProjectFromCsv uses a csv feeder to make a POST request for creating a sample project, storing it in the Gatling session.
  2. checkIfProjectOnPage makes a GET request and checks if the created project is in the requested page. If the project is not present in the page, the execution will stop here, since we use the method exitHereIfFailed after this.

With the scenario builder created, we can create our simulation by extending theSimulation class so Gatling can launch them. We must call the setUp method to register the simulation components and use the injectOpen method to define the amount of users you want to simulate. Finally with the .protocols() method you can attach the http protocol configuration we defined including aspects like common headers.

After the execution, Gatling generates a report detailing all the Simulation statistics, and various graphics that help us to understand how the tests and the endpoints are working.

Conclusion

In this article we described a full step-by-step guide for building Gatling tests from scratch using a sample application. We addressed the most common use cases and made some good examples of different scenarios and situations that you should want to test. As it can be easily appreciated, Gatling is a powerful tool that provides a flexible DSL 100% Java-based to build tests in a modular fashion and that, at the same time, can be easily extended with custom functions and behaviors if needed. This makes it a very scalable choice for writing stress tests within the Java ecosystem.

Andres Schuster
Software Engineer & Solver

Start Your Digital Journey Now!

Which capabilities are you interested in?
You may select more than one.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.