In the previous blog post I’ve described how to use Dataprovider of TestNG to assert the response time of the REST API endpoint when sending multiple parallel requests. In this blog post, I will describe how to do the same with Gatling, which is a more suitable tool for such kind of task.
About Gatling
Gatling is an open-source tool that allows you to make load testing of web applications. It provides a flexible DSL that makes possible to emulate real production user scenarios. For example, consider the following scenario: 100 users are sending requests to the service for 3 minutes, then the number of users decreases and stays for the next 2 minutes and then again increases for 5 minutes. The load that users put on the service is not constant, it is floating. This is a very common scenario, there are peaks and lows. Thus, just throwing a fixed number of requests to the service at the same time during a certain period of time - this is not how real production scenario looks like.
Gatling runs on JVM and is written in Scala. But there’s no need in Scala programming experience to write tests. The DSL that Gatling provides is self-expressive.
Initialize project with Gatling and Maven
Gatling has good integration with build tools like SBT and Maven. Though SBT is a popular choice for Scala, I have more experience with Maven and since there’s a plugin developed by Gatling developers, I decided to go with the tool which I know better.
Let’s create a new Maven project. To add Gatling to the project, add the following dependency:
<dependencies>
<dependency>
<groupId>io.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
<version>3.7.3</version>
</dependency>
</dependencies>
Since we’re using Maven, a gatling-maven-plugin
should be added. Put this block inside the build
section of
pom.xml
file.
<plugin>
<groupId>io.gatling</groupId>
<artifactId>gatling-maven-plugin</artifactId>
<version>4.1.0</version>
</plugin>
And the final thing, to be able to run Scala with Maven, add scala-maven-plugin
plugin.
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>4.5.6</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
Define service under test and testing scenario
Now, when all needed dependencies are set up. Next, we need to define the testing scenario.
For this demo, I will use one of the endpoints provided by Postman Echo service which is a free service that provides HTTP access for testing and demo purposes.
The scenario that we’re going to emulate will be as follows: during the period of 2 minutes, we will send 200
requests that will be distributed evenly during that period, next we will increase the load to 300 users during one
minute and finally will finish the scenario with 200 users during one minute. And we will set an assert that the
response time of each request should be no more than 800 ms and the status code of the response should be equal to
200 OK
.
This scenario is used just for demo purposes. In a real project, before starting to automate the scenario, analyze the potential load that is expected on your service and find out what scenario will suit your case.
Create a class and make this class to extend the Gatling’s Simulation
class.
That is mandatory for all classes that are expected to run the scenario.
Next, define some constants, like the base URL, query parameters and protocol configuration.
private val url = "https://postman-echo.com/get"
private val queryParams = Map("foo1" -> "bar1", "foo2" -> "bar2")
private def httpConfBuilder(envUrl: String): HttpProtocolBuilder = {
http
.baseUrl(envUrl)
.acceptHeader("application/json")
}
After some constants are set up, next define the scenario itself that will represent the actual business logic. Define here all the query and path params that are accepted by the service, request body and asserts (for response time, for example).
private val sendGetRequest = scenario("Get date from endpoint")
.exec(http("GET method")
.get("/")
.queryParamMap(queryParams)
.check(status is 200, responseTimeInMillis lte 800))
In this example, I’m sending a GET request to the particular endpoint of the
service (root in the example below). Define your custom name for the whole
scenario in scenario
method, next, set the name of the particular request in
exec
method and all the configuration of the request. Since the business logic
of a real project might assume sending different type of requests to different
endpoints with different asserts, it is possible to chain multiple requests
inside the one scenario.
Next, after scenario is defined, the set up should be done. The set up of the
scenario is the actual logic of how many request should be sent, how much time
the scenario should be executed, how the user load should be spread during the
execution. All of that is done using the setUp
method.
setUp(sendGetRequest.inject(
rampUsers(200) during(2 minutes),
rampUsers(300) during (2 minutes),
rampUsers(200) during(1 minutes)
)).protocols(httpConfBuilder(url))
Pass the scenario name as a method argument and define the load logic inside the
inject
method. When finish with defining the load logic, set the HTTP
configration to be used during the execution.
That’s it with the basic GET request with Gaatling. Run the following command to start the scenario execution.
mvn gatling:test
Upon the finish, Gatling will generate the HTML report with statistics and
graphics under target/gatling/${class_name}-${timestamp_of_execution}
.
Parameterized scenarios with POST request
In the previous section, I’ve described the simple scenario with the GET request. In this section I’m going to describe the scenario for the parameterized POST request.
Let’s first define a service under test and HTTP configuration. As an endpoint I will use another service from Postman.
private val url = "https://postman-echo.com/post"
private def httpConfBuilder(envUrl: String): HttpProtocolBuilder = {
http
.baseUrl(envUrl)
.acceptHeader("application/json")
}
I have a CSV file with a column that contains random ID numbers that will be
used as a parameter to a test scenario. Gatling has a feeder
feature that
feeds the test scenario with values from a defined feeder. There are available
several built-in feeders, check
official documentation for more
details.
To define a feeder for the CSV file, just use a csv
method from Gatling core
and provide a filename, located under src/main/resources
or
src/test/resources
:
val feeder = csv("random_strigs.csv")
Given the CSV file has a column header, then we can refer to this header to use values from this column. In the next example, I will use values from a CSV file to insert them into the query parameter map. Each request will be sent with its own value taken from the feeder.
private val sendPostRequest = scenario("Send POST request to endpoint")
.feed(feeder)
.exec(
http("POST method")
.post(url)
.queryParamMap(Map("id" -> "${id}"))
.check(status is 200, responseTimeInMillis lte 800))
"${id}"
in the query map points to the column header in CSV file. That will
take a value from the feeder and compose a URL of the following format:
/post?id=781f5efc84
.
As in the previous example, we will check for status code 200 and that the response time will be less than or equal to 800 ms.
And the set up of the scenario will be the same as with the GET method test.
Run mvn gatling:test
command and observe result upon completion.
Run multiple scenarios within one project
It is very often when you have multiple scenarios within the same project and
you want them to be executed one-by-one on a CI server. By default, Gatling
doesn’t allow more than one setUp
method per project. But that is configurable
in gatling-maven-plugin
.
Add the following configuration to the maven plugin:
<configuration>
<runMultipleSimulations>true</runMultipleSimulations>
</configuration>
Run the mvn gatling:test
command. That will execute all tests in the
project sorted by alphabetic order.
Conclusion
In this blog post, I showed how quickly and easily you can create your first test for a load testing with Gatling.
All code examples for this blog post can be found over on GitHub.