I have written about other performance testing tools for web applications before. Nevertheless I’d like to demonstrate a library for load testing web applications named Gatling in combination with the build tool Maven.
Gatling offers a nice Scala DSL, high performance using Akka, Netty and asynchronous IO and plug-ins for all modern build tools.
In the following tutorial I’m going to show how to record simulations using an HTTP proxy, rewriting simulations in Scala and running and reporting simulations with Maven.
Dependencies
Using Maven we need to add two dependencies to our project’s pom.xml: gatling-chats-highcharts and the gatling-maven-plugin.
<dependency>
<groupId>io.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
<version>2.2.0</version>
<scope>test</scope>
</dependency>
<plugin>
<groupId>io.gatling</groupId>
<artifactId>gatling-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>
Application under Test
We’re reusing a Java EE web application from my tutorial "Java EE: Setting up and Testing Form-Based JDBC Authentication with Arquillian and Maven".
It’s a simple web application secured with form-based authentication and we’re adding some additional users so basically to run the web application with multiple existing users, we simply need to download the sources from my tutorial "Performance Testing a Multiuser Web Application with JMeter and Maven" and run the following command:
mvn clean package org.glassfish:maven-embedded-glassfish-plugin:3.1.1:run
Afterwards we may access our application at this location: http://localhost:8080/hascode
Setting up Gatling
Download Gatling from http://gatling.io/#/download
export GATLING_HOME=/path/to/gatling-charts-highcharts-bundle-2.2.0
Starting the recorder
$GATLING_HOME/bin/recorder.sh
Running the Simulations with CLI
$GATLING_HOME/bin/gatling.sh
Recording via HTTP Proxy
Now we want to generate a simulation for our running web application.
We’re starting the Gatling recorder as described above and configure our browser to use this proxy.
When doing this more often, addons like FoxyProxy are really helpful to switch between different proxy servers.
We may fine-tune the settings for the recorder e.g. to filter ressources like images and cascading-style-sheets, use a designated package for the tests etc.
Then we’re clicking “start” in the Gatling recorder application and we’re using our browser to click through the application while Gatling is recording.
When we’re done with recording we’re clicking on “Stop & Save” and Gatling generates a Scala file with our simulation.
Generated Simulation
This is what our generated simulation looks like.
class RecordedSimulation extends Simulation {
val httpProtocol = http
.baseURL("http://localhost:8080")
.inferHtmlResources(BlackList(""".*\.css""", """.*\.js""", """.*\.ico""", """.*\.png""", """.*\.gif"""), WhiteList())
.acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
.acceptEncodingHeader("gzip, deflate")
.acceptLanguageHeader("en-US")
.userAgentHeader("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0")
val headers_0 = Map("REMOTE_USER" -> "admin")
val uri1 = "http://localhost:8080/hascode"
val scn = scenario("RecordedSimulation")
.exec(http("request_0")
.get("/hascode")
.headers(headers_0))
.pause(25)
.exec(http("request_1")
.post("/hascode/j_security_check")
.headers(headers_0)
.formParam("j_username", "admin")
.formParam("j_password", "test"))
.pause(3)
.exec(http("request_2")
.get("/hascode/rs/session")
.headers(headers_0))
setUp(scn.inject(atOnceUsers(1))).protocols(httpProtocol)
}
Though this simulation works, we should refactor it and add features like checks and reading user credentials from a CSV file.
Rewriting the generated Simulation
We’re optimizing the generated code by ..
-
encapsulating each page request in an object – a page object. This allows us to chain our requests and fine tune page specific checks and parameters in one place.
-
reading user credentials from a CSV file and passing them to the login screen
-
modifying our simulation to run two different scenarios
-
configuring our simulation to ramp up a specific amount of parallel users over time
-
adding a global assertion that no request may have failed
package com.hascode.tutorial
import scala.concurrent.duration._
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.jdbc.Predef._
class RecordedSimulation extends Simulation {
val httpProtocol = http
.baseURL("http://localhost:8080")
.inferHtmlResources(BlackList(""".*\.css""", """.*\.js""", """.*\.ico""", """.*\.png""", """.*\.gif"""), WhiteList())
.acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
.acceptEncodingHeader("gzip, deflate")
.acceptLanguageHeader("en-US")
.userAgentHeader("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0")
object HomePage {
val home = exec(http("StartPage")
.get("/hascode"))
.pause(25)
}
object LoginPage {
val loginProperties = csv("user-login.csv").random
val login = feed(loginProperties).exec(http("LoginPage")
.post("/hascode/j_security_check")
.formParam("j_username", "${username}")
.formParam("j_password", "${password}"))
.pause(3)
}
object UserInfoPage {
val info = exec(http("UserInformation")
.get("/hascode/rs/session"))
}
val scenario1 = scenario("JavaMultiuserScenario")
.exec(HomePage.home, LoginPage.login, UserInfoPage.info)
val scenario2 = scenario("JavaMultiuserScenario")
.exec(HomePage.home, LoginPage.login)
setUp(scenario1.inject(rampUsers(10) over (10 seconds), rampUsers(2) over (10 seconds)))
.protocols(httpProtocol)
.assertions(global.successfulRequests.percent.is(100))
}
User Credentials CSV File
These are our users in a comma-separated-file named user-login.csv:
username,password
admin,test
ted,foo123
lisa,xyz123
marge,redrum
bart,eatmyshorts
Running the Simulation with Maven
Now we may run the simulation using our Maven build tool like this:
$ mvn clean gatling:execute
[..]
Simulation com.hascode.tutorial.RecordedSimulation started...
================================================================================
2016-04-25 22:58:20 5s elapsed
---- JavaMultiuserScenario -----------------------------------------------------
[------------------------- ] 0%
waiting: 8 / active: 4 / done:0
---- Requests ------------------------------------------------------------------
> Global (OK=8 KO=0 )
> StartPage (OK=4 KO=0 )
> StartPage Redirect 1 (OK=4 KO=0 )
================================================================================
[..]
================================================================================
2016-04-25 22:58:59 44s elapsed
---- JavaMultiuserScenario -----------------------------------------------------
[##########################################################################]100%
waiting: 0 / active: 0 / done:12
---- Requests ------------------------------------------------------------------
> Global (OK=60 KO=0 )
> StartPage (OK=12 KO=0 )
> StartPage Redirect 1 (OK=12 KO=0 )
> LoginPage (OK=12 KO=0 )
> LoginPage Redirect 1 (OK=12 KO=0 )
> UserInformation (OK=12 KO=0 )
================================================================================
Simulation com.hascode.tutorial.RecordedSimulation completed in 43 seconds
Parsing log file(s)...
Parsing log file(s) done
Generating reports...
================================================================================
---- Global Information --------------------------------------------------------
> request count 60 (OK=60 KO=0 )
> min response time 1 (OK=1 KO=- )
> max response time 18 (OK=18 KO=- )
> mean response time 7 (OK=7 KO=- )
> std deviation 4 (OK=4 KO=- )
> response time 50th percentile 7 (OK=7 KO=- )
> response time 75th percentile 10 (OK=10 KO=- )
> response time 95th percentile 17 (OK=17 KO=- )
> response time 99th percentile 17 (OK=17 KO=- )
> mean requests/sec 1.364 (OK=1.364 KO=- )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms 60 (100%)
> 800 ms < t < 1200 ms 0 ( 0%)
> t > 1200 ms 0 ( 0%)
> failed 0 ( 0%)
================================================================================
Reports generated in 0s.
Please open the following file: /data/project/gatling-tutorial/target/gatling/recordedsimulation-1461617895727/index.html
Global: percentage of successful requests is 100 : true
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[..]
After the successful run (might take a few minutes), the Gatling reports are available in the target/gatling/ directory.
Load Testing Atlassian JIRA or Confluence
If you’d like to see other Gatling tests, please feel free to checkout these two branches of my Git repository:
-
feature/confluence-simulation: Simulation for Atlassian’s Confluence Wiki
-
feature/jira-simulation: Simulation for Atlassian’s Issue Tracker JIRA
Tutorial Sources
Please feel free to download the tutorial sources from my GitHub repository, fork it there or clone it using Git:
git clone https://github.com/hascode/gatling-tutorial.git
Alternative: JMeter
An alternative to Gatling is JMeter, if interested in using JMeter with Maven, please feel free to read my tutorial Performance Testing a Multiuser Web Application with JMeter and Maven.