Load Testing Using Gatling and Java #1

When it comes to load testing, Gatling stands out as a robust and efficient tool designed to handle high loads and generate insightful reports. Known for its scalability and versatility, Gatling allows developers and QA engineers to simulate various scenarios to test application behavior under heavy load. While Gatling initially gained popularity for its Scala-based DSL, it now also supports Java, making it accessible to an even wider audience. In this post, we'll go over how to set up performance testing using Gatling with Java, covering its advantages, core features, and a step-by-step guide to creating a basic load test.


Why Choose Gatling for Performance Testing?

Here are some key reasons to use Gatling for performance testing:

  1. Detailed Reporting: Gatling provides extensive reports with insightful metrics such as response time, latency, and error rates, all visualized in easy-to-read graphs.
  2. Scriptability with Java: The Java DSL allows developers familiar with Java to create readable, maintainable test scripts without needing to learn Scala.
  3. Integration with CI/CD: Gatling is designed to integrate well with CI/CD tools like Jenkins, Gitlab enabling automated performance testing as part of the development pipeline.
  4. Test-as-code: Gatling is load-test-as-code. You can literally download sample code, add a few tests in the template, and perform load testing from your local machine within 10 minutes.

Setting Up Gatling with Java

To start using Gatling in a Java project, follow these steps:

Prerequisites

  1. IDE: Use IntelliJ IDEA or Eclipse.
  2. JDK: Gatling supports 64-bit OpenJDK LTS (Long Term Support) versions: 11, 17, and 21. Note that 32-bit systems, OpenJ9, and other JVMs are not supported. Gatling recommends using the Azul JDK.

This guide will demonstrate setup using IntelliJ and Gradle, but you can use any tools that suit your workflow. For a quick setup, you can download the pre-configured zip files for Gradle or Maven and start scripting immediately. However, for this guide, we’ll be setting up the project manually to provide a deeper understanding.

The Gradle plugin lets you execute Gatling tests directly from the command line without needing the full Gatling bundle. It also enables you to package your simulations for Gatling Enterprise. This plugin integrates Gatling with Gradle, making it easy to use Gatling as a testing framework within your Gradle build system. This plugin requires at least Gradle 7.1.

Create a new project in Intellij with gradle as build system.


Add gatling gradle plugin to your build gradle file. Latest gatling gradle plugin at the time of writing this blog was 3.12.0.3.

Create a src directory ‘gatling’ if not already created. The folder structure would look like this.


Next, we will develop some basic simulation scripts for CRUD operations. I’ll be using json-server to set up mock APIs. For more information about json-server, you can refer to this repository. Now, let's create our first simulation class. This simulation will perform create, update, retrieve, and delete operations on a resource.

Sample Requests:

POST Request


PUT Request


You can find the repository here. Now, let's go over the key components of a Gatling simulation file.

All simulation classes and feeder files should be placed within the gatling module of the src directory. Each simulation class must extend the Gatling abstract class Simulation, which is part of the io.gatling.javaapi.core package.

Feeders

Feeders are used to pass test data to the gatling simulation scripts. Feeders can be a csv file, json file, tsv file or even lists or arrays. We also define a build-in strategy when using feeders. In this particular project we are using a circular() strategy, which means gatling will go back to the top of the sequence once the end is reached. To learn more about feeders you can refer to the gatling documentation.

HttpProtocolBuilder


The HTTP Protocol Builder is used to configure HTTP request settings, such as setting up base URLs, timeouts, headers, cookies, and other HTTP-specific configurations. This allows you to customize how Gatling interacts with the HTTP endpoints during your load tests.

The HTTP protocol configuration is defined using the http object in Gatling, and it is typically assigned to a variable in our case its httpProtocol, which is then used in the setUp block to configure the simulation.

baseUrl

baseUrl("http://localhost:3000")

This sets the base URL for all HTTP requests made in the simulation. In this case, all requests will be sent to http://localhost:3000, and any relative URLs you specify in subsequent requests will be appended to this base URL.

      For example, if you make a request like .get("/api/data"), the full URL would be http://localhost:3000/api/data.

acceptHeader

acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")

This method sets the Accept header for the HTTP requests. The Accept header informs the server about the types of content the client is willing to receive.

      The value indicates the types of responses the client is willing to accept, with preferences:

      text/html: The client prefers HTML responses.

      application/xhtml+xml: The client also accepts XHTML responses.

      application/xml;q=0.9: The client accepts XML responses, but with a slightly lower preference (q=0.9).

      */*;q=0.8: The client will accept any type of content (*/*), but with a lower preference (q=0.8).

      This is a standard mechanism used in HTTP to handle content negotiation, where the server can send a response in different formats based on what the client is willing to accept.

 userAgentHeader

userAgentHeader("Mozilla/5.0 (Linux; Android 9.0; Galaxy S9 Build/
OPR1.170623.011)AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/
111.0.0.0 Mobile Safari/537.36")

This method sets the User-Agent header for the HTTP requests. The User-Agent header identifies the client software making the request to the server. It's a way to simulate requests coming from a specific type of device or browser.

      In this example:

      Mozilla/5.0: Indicates that the client identifies as a Mozilla browser (common in User-Agent strings).

      Linux; Android 9.0; Galaxy S9 Build/OPR1.170623.011: Specifies that the client is running on Android 9.0 (on a Galaxy S9 device).

      AppleWebKit/537.36: This indicates the browser is using the WebKit rendering engine (which is common for browsers like Chrome and Safari).

      KHTML, like Gecko: Indicates compatibility with the Gecko layout engine (used by Firefox).

      Chrome/111.0.0.0: Specifies that the client is running Chrome version 111.

      Mobile Safari/537.36: Indicates the Safari mobile browser version (again, WebKit-based).

      This header is useful for simulating requests from a specific browser or device type. By specifying a mobile browser User-Agent, this configuration simulates requests from a mobile device (Nexus 5 running Android) rather than a desktop browser.

ChainBuilder


The code uses a ChainBuilder to define a series of steps (also called actions) in a virtual user's scenario. Below is a detailed breakdown of the different parts of the code.

exec():

      exec() is a Gatling method used to define an action or a series of actions that will be performed by a simulated user. This action can be HTTP requests, pauses, assertions, or other operations.

      exec() is used here to define the action of sending an HTTP request to create a car.

http("Create car"):

      http("Create car") is a description of the HTTP request. The string "Create car" is a name you give to the request, which can be useful for identifying requests in the Gatling reports.

      This is not sent as part of the request itself but is used for reporting purposes.

post("/cars"):

      This specifies the HTTP POST request to be sent to the endpoint /cars.

      POST is typically used to create a new resource on the server. In this case, it seems you're sending data to create a new car resource.

body(StringBody(...)):

      The .body() method specifies the body of the HTTP request. The body content is being constructed as a JSON object.

      The StringBody method allows you to send raw JSON (as a string) as the request body.

      The JSON body you’re sending has placeholders like #{BRAND}, #{NAME}, and #{VARIANT}, which are feeder variables that will be replaced with actual values from the csv when the request is executed.

      The placeholder #{randomInt(5,100)} is a dynamic value that generates a random integer between 5 and 10 to be used as the id of the car.

check(status().is(201)):

      The .check() method is used to define an assertion or validation on the HTTP response.

      In this case, status().is(201) checks that the HTTP response status code is 201, which means that the resource was successfully created.

      If the response code is not 201, this check will fail, and the request will be marked as a failure.

check(jmesPath("id").saveAs("carId")):

      This check uses JMESPath (a query language for JSON) to extract a value from the response body.

      It searches for the "id" field in the response JSON and saves it as a session variable called carId.

      The saveAs("carId") part tells Gatling to store the value of the "id" from the response in a session variable called carId, which can later be used in subsequent requests or actions.

In the Cars.java simulation example, I have created multiple chainbuilders like updateCar, getCar, getCarByBrand, deleteCar.

ScenarioBuilder

In earlier versions of gatling ScenarioBuilder was defined like:


New version allows both ways. 
In Gatling, a ScenarioBuilder is a key component used to define the sequence of actions that virtual users (VUs) will perform during a simulation. It represents the behavior of a virtual user and consists of one or more actions (such as HTTP requests, pauses, checks, etc.) that are executed in a specified order.

While ChainBuilder defines a chain of actions, ScenarioBuilder defines a complete scenario in which one or more chains of actions are executed. A ScenarioBuilder contains a sequence of steps that simulate user interaction with a system.

The scenario "Get user":

      Feeds data from the feeder into the scenario.

      Executes five sequential actions: createCar, updateCar, getCar, getCarByBrand, and deleteCar, which represent various HTTP requests for creating, updating, retrieving, and deleting a car.

Setup:

The injection profile is the final element of a Gatling simulation and is defined within the setUp block.

 setUp():

      This is where the simulation is configured. The setUp function defines how the scenario (scn) will be executed, including how virtual users are injected into the system and the protocols (e.g., HTTP configuration) they will use during the test.

      It’s a key function in Gatling simulations that defines the overall execution plan for the load test.

scn:

      This is the scenario that has been previously defined in the simulation, which outlines the sequence of actions (e.g., HTTP requests, pauses, etc.) that the virtual users will execute.

injectOpen:

      Injection profiles define how virtual users will be introduced to the system during the simulation.

      injectOpen() is one of several injection methods in Gatling and is used to specify a constant rate of user injection over a given duration. To know more about injection profiles refer.

 constantUsersPerSec(1):

      constantUsersPerSec(1) defines the rate of virtual users to be injected into the system. In this case, 1 user per second will be injected.This means that every second, 1 new virtual user will start executing the scenario defined by scn. If you had specified constantUsersPerSec(5), for example, 5 new users would start every second.

during(Duration.ofSeconds(9)):

      during(Duration.ofSeconds(9)) specifies how long the virtual users should be injected at the defined rate.

      In this case, the injection of 1 virtual user per second will continue for 9 seconds.

      After the 9 seconds have passed, the injection of new users stops, and the test proceeds with the virtual users that have already been started.

protocols(httpProtocol):

      .protocols() is used to configure the protocols (such as HTTP, WebSocket, etc.) that the virtual users will use to interact with the system under test.

      httpProtocol is a variable that holds the HTTP configuration (such as the base URL, headers, etc.) for the simulation.

      This ensures that all virtual users created by this simulation will use the same HTTP protocol settings, like the base URL, headers, user-agent, etc.

 

Running the Simulation

The gatling-gradle plugin comes with a default task to run the simulation. To run the simulation on a macOS or linux system use the following command, the command runs in interactive mode

./gradlew gatlingRun

If there is only one simulation the task will run the simulation. If there are more then one simulation, the command will provide the option to choose.

You can also provide the fully qualified class name if you want to run one simulation.

./gradlew gatlingRun --simulation qajournal.Cars

To run all the simulation sequerntially you can use command

./gradlew gatlingRun --all

That’s all for this article. In the nextone, we’ll build a framework around Gatling and explore how to create a custom Gradle task to run the simulations. Happy testing! 🚀


Post a Comment

Previous Post Next Post