Load Testing Web Applications with Gatling and Maven

May 6th, 2016 by

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.

Gatling Test Results

Gatling Test Results

 

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.

Using FoxyProxy for Firefox.

Using FoxyProxy for Firefox.

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.

Gatling Simulation Recorder

Gatling Simulation Recorder

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.

Gatling Recorder capturing

Gatling Recorder capturing

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.

Gatling Test Results

Gatling Test Results

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:

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/gatling-tutorial.git

Resources

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“.

Tags: , , , , , , , , , , ,

Search
Tags
Categories