Load testing. These two words can send shivers to the hearts of developers and network-folk alike. Designed to hit a new app with enough user requests to crush it like a beer can, often, load testing is an involved and tedious process. And it can easily get worse as a result of using the wrong tool, or toolkit, for the job.

Several Java load testing tool kits exist, falling into two categories: open source (free) and paid. There have been many good reviews for paid toolkits, most notably BlazeMeter and LoadRunner, but they get pretty expensive. Open source is a nice alternative if you can find the proper tooling. At Make & Build, the open-source route is preferred for more direct control of testing scenarios.

While a handful of free load testing suites are available, the largest and most popular is JMeter. While time-tested and proven, JMeter can be clunky and hard to set up. Users requiring a load tester with a large set of options may find JMeter helpful. Another proven option is Grinder (not Grindr), but it comes with its own difficulties and some dwindling community support. Luckily, a full testing suite isn’t always necessary. So when the only need is testing an app against page requests from a large number of users, there exists a better option: Gatling.

While not written in Java, Gatling is written in Java’s functional cousin, Scala. Not a testing suite, Gatling is great being just what it is, a load-testing program. Although written in Scala, it is generic enough to be used on any web application, including single page apps. Setup is easy in comparison to other OSS options, and even better, Gatling is backed by a growing open-source community. A short “How-To” on Gatling test setup and some brief examples will show how to get load testing merged into your continuous integration pipeline.

 

Basic Concepts

Scenarios

The functionality of Gatling stems from scenarios. A scenario is a functional statement that tells the Gatling runner what to do. An example to illustrate:

1
2
3
4
5
6
7
8
9
10
11
 val testExample = scenario("Landing Page Test")
 
      .exec(http("Access M&B";).get("http://github.com"))
 
       .headers(headers_32)
 
      .pause(2, 3)
 
      .exec( http("Search for 'makeandbuild'").get("http://github.com/search").queryParam("q","makeandbuild"))
 
      .pause(2))

 

As above, a scenario is fairly straight forward. After declaring the scenario, there are two execution statements broken up with two pauses. GETs are usually pointed at a url and can be modified with query parameters. In this case Gatling would output the url http://github.com/search?q= makeandbuild . The entire test is loaded into an object called testExample.

 

Setup and Modifiers

1
            setUp(testExample.users(3000).ramp(15).protocolConfig(httpConf))

The setup statement allows configuration and execution of the load test. The code above uses testExample object and sets the users to 3000. Notice the ramp modifier. This tells Gatling how quickly to increase from one user to 3000, in this case over 15 seconds. This timing helps better simulate real world user reactions. Also note passing of a protocolConfig, httpConf. This represents the settings around the browser and web page. Below, the code includes items such as the base url, acceptable headers, language, and browser/OS details. Many of these items can be stripped to make the test more generic, but most of the example below is produced if you use the recorder.

1
2
3
4
5
6
7
8
9
10
11
12
13
                        val httpConf = httpConfig
 
                                    .baseURL("http://s3.amazonaws.com")
 
                                    .acceptHeader("*/*")
 
                                    .acceptEncodingHeader("gzip, deflate")
 
                                    .acceptLanguageHeader("en-US,en;q=0.5")
 
                                    .connection("keep-alive")
 
                                    .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:30.0) Gecko/20100101 Firefox/30.0")

Headers and Request Bodies

1
2
3
4
5
6
7
8
9
                        val headers_32 = Map(
 
                                    "Accept" -> """application/json, text/javascript, */*; q=0.01""",
 
                                    "Content-Type" -> """application/json""",
 
                                    "Origin" -> """http://s3.amazonaws.com"""
 
            )

Headers are exactly that, the headers sent with the request, typically including basic information, namely content and acceptance types. A nice point, it is possible to use one header for all requests or make a unique header for each at the test-writer’s discretion.

{“formFieldTemplateId”:5,”value”:””,”registrationId”:11,”id”:73}

Request packets are stored as JSON documents. REST users should find this as no surprise. The JSON document is then stored as a separate text file to avoid cluttering the test. It can be found in the user-files/request-bodies directory.

Feeders and Checks

Feeders allow you to set the header/session info differently for users based on a CSV file. For instance, a CSV file with the login and session info for twenty users can feed into a test such that each user receives different info. These variables allow for testing not just regular users, but admin and developer logins as well.

Checks are very similar to Assert statements in Java. Taking into account what is expected, a check will compare this value against what is received.

 

Recorder

Writing scenarios is easy, but never easy enough! That’s when it’s time to pull out the recorder and point it at the web page. Simply go to the bin directory and start up recorder.sh (You’re using a unix- based system right?! Don’t worry, there’s a .bat file too).

gatlingRecorderExample

Most of these settings can stay the same. Clicking “Start” will open the next screen, which shows a rolling display of the requests being created. The real magic comes in the browser setup. Using the recorder requires running the browser through a proxy. The proxy will start up once the “Start” button is clicked. While Chrome is the go to choice for debugging, setting up a proxy may prove difficult, and results are often varied if it even picks up the Gatling proxy. To this end Firefox has shown better reliability for all recording purposes. Go to Preferences → Network → Settings and fill in the fields as shown below.

proxySettings

Users may need to restart Firefox or Gatling the first time and also between tests. Also, the recorder needs to be up in order to use the Firefox browser.

 

Running Tests

The tests have been produced and need to be run. Run this from the bin directory:

Unix -

1
./gatling.sh -s {name of your test}

Windows –

1
gatling -s {name of your test}

It is not necessary to include .scala as the test name refers to the class name not the file name, so myTest.scala would simply be “-s myTest”.

 

Reports

A large number of reports are generated every time a Gatling test is run. So many, in fact, that it would be impractical to demo them, but check them out here.

 

Jenkins Integration

 

Luckily, Gatling comes with an easy to install plugin for the Jenkins integration server. A simple download and install through the Jenkins UI and reports should automatically generate and be displayed for each test. Testers can access the full set of reports mentioned above, giving as much detail for run times, failures, and requests as any individual could need.

 

While possible and perfectly reasonable to integrate the Gatling scripts into your application, this has been found to be unnecessary. Simply loading the Gatling program onto Jenkins and running it with a bash shell script is easy enough, see the sample below. Notice the “ulimit -Hn”. This is needed on the Linux server since the number of outgoing threads was limited, causing the test to throw exceptions when it reaches over 1000 users.

 

1
2
3
4
5
6
7
8
9
            #!/bin/bash --login
 
            ulimit -Hn
 
            echo $USER
 
            cd /opt/gatling/gatling-charts-highcharts-1.5.5/bin
 
            ./gatling.sh -s ExampleLoadTests -rf /var/lib/jenkins/jobs/jenkins-load/workspace/results

When setting up the plugin, sometimes Gatling sends the reports to a directory the Gatling-Jenkins plugin was not expecting. This can depend on how Jenkins and Gatling are set up.

For instance, adding an -rf param to the shell script can redirect the report results to the directory Jenkins is expecting. This is because the Jenkins plug-in expects the results in a particular workspace.

 

Conclusion

After working with JMeter in the past, using Gatling is a breeze. Load tests are written quickly and are very effective. For instance, this was first used on a single-page, Backbonejs-based app with a Java REST backend. Now when a new page is added or functionality of an existing page is increased, we can create a load test to check the bases efficiency of the REST services. This often helps identify problem areas before they even go out into the wild, without using expensive paid load-testing tools and gaining the advantages of a robust open-source platform.

 

Relevant Links

Gatling home - http://gatling-tool.org/

GitHub Wiki - https://github.com/excilys/gatling/wiki/Introduction

GitHub Project - https://github.com/excilys/gatling