Starting with Gatling …
By: Md. Yusuf Ansari
Tags: Performance Testing, Scala, Gatling

Enough with the theory, time for some action!

Let’s setup a development environment and play around with the script generated with the Recorder UI and understand the Gatling script structure.

HTTP protocol configuration

This will define your base URL that you will be running your tests against. Also, you can define some other configurations such as user agent, language header, connection and so on.

We will add some imports and extend the class with Simulation. We have set our baseURL to http://cheeze-flight-booker.herokuapp.com. So, every time we run a request, it will use our baseURL followed by other configurations such as acceptHeader, acceptEncodingHeader, acceptLanguageHeader, connection and userAgentHeader.

We have also added SilentResources, this will prevent all the resources or request to appear in the report which are not need.

val httpProtocol = http

.baseUrl("http://cheeze-flight-booker.herokuapp.com")

.inferHtmlResources()

.acceptHeader("image/avif,image/webp,image/apng,image/*,*/*;q=0.8")

.acceptEncodingHeader("gzip, deflate")

.acceptLanguageHeader("en-US,en;q=0.9")

.userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36")

.silentResources //SilentResources

Header Definition

This provides the headers for each request that will be sent to the server. This is relevant because the headers also add a bit of load to the server you are testing.

The headers are generated automatically if you are using a Gatling recorder. (In case of API, the user know the headers being passed and can pass it in multiple different fashion as depicted here).

We will pass these headers variables in the scenario definition wherever required.

val headers_0 = Map(

"Accept" -> "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",

"Upgrade-Insecure-Requests" -> "1")



val headers_1 = Map("Pragma" -> "no-cache")

Scenario definition

The core of your test! A Scenario is a group of actions (GET, POST, etc.) that will be executed in order to simulate a user interaction with your application.

In this step, we will clear the browser cache and cookies before scenario execution, so that every virtual user acts as fresh user. This will result in proper load data and hence increase the load on server as much as possible.

We will also add checks on status code and each element present on the page.

val scn = scenario("Access Home Page") //Starting of a scenario //Clear browser cache and cookie

.exec(flushHttpCache)

.exec(flushSessionCookies)

.exec(flushCookieJar) //Starting of scenario execution and making a GET request to access the HomePage

.exec(http("HomePage")

.get("/")

.headers(headers_0) //Adding Headers to our execution

.resources(http("request_1")

.get("/favicon.ico")

.headers(headers_1)) //Checks to verify status code and Page Elements .check(status.in(200,201,202,304))

.check(status.not(404))

.check(css("h1:contains('Flights')").exists)

.check(substring("from").find.exists)

.check(substring("to").find.exists)

.check(substring("date").find.exists)

.check(substring("passengers").find.exists)

.check(css("input[value='search']").exists))

We can also print the Response body for a session

.check(bodyString.saveAs("BODY")))

.exec{

session =>

println(session("BODY").as[String])

session

}

//This will print the response body in our console which can be used later for debugging purpose.

or, we can also print any variable which we are passing as feeders for a particular scenario.

val randomEmailFeeder = Iterator.continually(Map("randomEmail" -> (Random.alphanumeric.take(20).mkString + "@ggmail.com")))

val randomNameFeeder = Iterator.continually(Map("randomName" -> (Random.alphanumeric.filter(_.isLetter).take(5).mkString.toLowerCase))) //Passing Feeders/Data to execute flight booking for multiple users. .feed(randomEmailFeeder)

.feed(randomNameFeeder)

.exec(http("BookFlights")

.post("/bookings")

.headers(headers_12)



.formParam("booking[passengers_attributes][0][name]", "${randomName}")

.formParam("booking[passengers_attributes][0][email]", "${randomEmail}")) .exec{

session =>

println(session("randomName").as[String])

session

}

Simulation definition

This defines the load (amount of users) that will concurrently execute your scenario for a period of time.

This is where we will define the load injected to the server and also add Assertions to verify some statistics.

We will also add assertions to verify the statics such as max response time and successful request percentage.

1st assertion — The cumulative response time of all the requests in the simulation should be less than 1000

2nd assertion — successful request percentage should be 100%

setUp(scn.inject(rampUsers(1000) during (15 seconds))).protocols(httpProtocol)

.assertions(

global.responseTime.max.lt(1000),

global.successfulRequests.percent.is(100)

)

This will inject 1000 users distributed evenly on a time window of 15 seconds for a particular scenario.

Once the script has finished running you will get a detailed report along with the statistics like pass and fail of each request, response time, std deviation, response time percentile, along with the assertion response. You will also get a link to PDF format of the report.

Curious to see the report, run the script using the following command:

mvn gatling:test -Dgatling.simulationClass=GatlingPractice

OR

Just right click on Engine under Scala directory and click on Run ‘Engine’ and follow the steps from the terminal.