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:
- Detailed Reporting: Gatling provides extensive reports with insightful metrics such
as response time, latency, and error rates, all visualized in easy-to-read
graphs.
- Scriptability with Java:
The Java DSL allows developers familiar with Java to create readable,
maintainable test scripts without needing to learn Scala.
- 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.
- 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
- IDE: Use IntelliJ IDEA or Eclipse.
- 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! 🚀