Photo by NoWah Bartscher on Unsplash

Photo by NoWah Bartscher on Unsplash

Gatling load-testing tool


Article from ADMIN 67/2022
Generate load on servers and services with the Gatling load-testing tool.

When questioned at work about how the test tool Gatling [1] compared with the somewhat old JMeter, I was at a loss for words. However, it did give me the opportunity to learn more about Gatling and eventually present another possible tool in the arsenal against weak servers and services.

In retrospect, I'm not sure how I missed Gatling, which is no recent upstart. Gatling's first stable release was in 2012, and only a few years later, its founder created Gatling Corp. to develop and maintain the software. Over the years, Gatling morphed into a product with both open source and Enterprise variants that can hold their own against other test tools.

Yet, Gatling has taken a significantly different path from some of the other load-testing products such as JMeter and LoadRunner. With Gatling, you don't have a custom IDE for development. Instead, you use the Gatling framework in your favorite editor to write your own test script in Scala.

Getting Started

All Gatling tests start pretty much the same, by subclassing from the Scala Simulation class. The simulation is essentially a collection of individual requests that a user or program would normally make. Because your test is a program, you are given the flexibility of creating variables, constants, and methods, and because you are using a proper programming language, you can create your own library of support objects or methods.

Your performance tests can include one or more classes, but you can create and include a lot of regular Scala objects, as well. Scala also supports an object type that appears to be similar to a class, but it is more akin to a singleton than a normal Java or C++ class.

You don't have to be a Scala programmer to enjoy the freedom that Gatling provides. Most developers, despite having a favorite language or toolset, can easily learn enough Scala to create their own test scripts in a few hours. Because the test script is pure source code, you receive extra advantages that are commonly enjoyed with normal software development: the use of common developer tools such as git, diff, and grep.

The first five lines of Listing 1 assign the class to a package and include all of the necessary reference information for the program. Of course, this process is reminiscent of Java. One of the neat technology choices that was made is that the Scala code is compiled by the Java compiler and then uses the Java Virtual Machine (JVM) installed on your computer.

Listing 1

Simple Gatling Example

01 package com.mypackagename
03 import Scala.concurrent.duration._
04 import io.gatling.core.Predef._
05 import io.gatling.http.Predef._
07 class BlazeDemoV1 extends Simulation {
09   var qualifiedhost = ""
10   var proxyport = 0
11   var proxyhost = "not set"
13   // headers for HTTP call
14   val headers = Map(
15     "Accept" -> "text/html",
16     "User-Agent" -> "LinuxMagazine/1.0.1",
17     "Accept-Encoding" -> "gzip, deflate",
18     "Connection" -> "keep-alive",
19     "DNT" -> "1" )
21   // http connector
22   var httpProtocol = http
23     .baseUrl(qualifiedhost)
25   if (proxyport > 0)
26     httpProtocol = httpProtocol.proxy(Proxy(proxyhost,proxyport))
28   val scn = scenario("BookFlight")
29     .exec(http("step_1")
30       .get("/")
31       .headers(headers)
32       .check(
33     .exec(http("step_2")
34       .get("")
35       .headers(headers)
36       .check(
37     .pause(5)
39   setUp(
40     scn.inject(atOnceUsers(1))
41   ).protocols(httpProtocol)
42 }

This one feature alone allows you to use any of the existing standard Java libraries or even your own custom code. Yet Java code is subtly different in syntax, so even using standard calls to read files will look fairly foreign once completed.

Line 7 defines the class file, which inherits from the Scala base class Simulation, and lines 9-11 define a few variables. In a proper production-quality script, these variables would probably be replaced with values that are passed in or perhaps read from a configuration file. The proxy configuration is not being used in this example, so simply setting these variables for your proxy will allow you to run the script over the proxy without any further modifications.

Lines 13-19 define the key pairs to be used as header values for HTTP statements. This collection has been defined as a constant with the keyword val, whereas the variables in the previous lines are re-assignable because they use the keyword var.

The framework does have the http object, which encapsulates all of the logic for connections between machines over the Internet. Instantiating a variable with quite a number of different parameters is possible with this class. The most important parameter is the URL of the machine to which to connect. Just like with other languages, additional method calls can perform additional variable setup. Lines 21-26 demonstrate how to create the HTTP protocol variable and how to override it in favor of a proxy server, if one is needed.

Lines 28-37 show the creation of a scenario that is just a list of all the different statements that will be called. The scenario test will be run again and again. In this example, one of the calls uses the default URL that has been set up in the HTTP protocol. The second call uses a different URL that has no connection to the URL that was used during object creation. Both of these statements use predefined header values but could just as easily be different sets for different calls. Each of these statements check that the call receives return code 200, indicating a successful call.

The final and most important part of this script is lines 39-41. All lines up to this point define and prepare a test scenario, but in these three final lines, the user scenario is executed by the framework. In this particular code, the test is only run once with a single user. Although not a very realistic load test, it is a perfect smoke test to verify that everything is running correctly.

Gatling provides quite a number of ways to create different load scenarios. Listing 2 shows a more complex setup that includes two different scenarios running in parallel. Each of these scenarios use different methods for generating user load.

Listing 2

Parallel Load Tests

01 setUp(
02   scn1.inject(
03     constantConcurrentUsers(2).during(60.seconds),
04     rampConcurrentUsers(2).to(4).during(10.seconds)
05   ).protocols(httpProtocol),
06   scn2.inject(
07     constantUsersPerSec(2).during(15.seconds),
08     rampUsersPerSec(2).to(4).during(10.seconds)
09   ).protocols(httpProtocol)
10 )

Point-and-Click Testing

Not everyone is a natural-born software developer. However, quite a few people who might not be able to create a program from scratch can make small modifications to or extend existing programs.

Gatling provides a crutch for people who either cannot write their own scripts or who want to lighten their load, by providing a proxy server that will record the steps that pass through it (Figure 1).

Figure 1: Gatling proxy server recorder setup.

To capture a URL and what you do while at that URL, simply start up and point your web browser to the proxy server. The information is then processed into a Scala source file and saved with a class name you provide. This output is saved into the default location for Scala simulations.

To use this recorder, just press the Start button, which opens another window (Figure 2) that lets you follow along as you select pages in your web browser.

Figure 2: Proxy recorder window.

A side effect for scripts that have been created by the recorder can be seen in Listing 3. That is, the code is pretty difficult to read because most modern web pages have a lot of resources, Java scripts, or other frameworks. Everything that is downloaded by the website will also be downloaded in tests created with the proxy recorder. These resources are necessary for a web page but might not be required for performance testing.

Listing 3

Proxy Recorder Excerpt

01 val scn = scenario("acmewebsite")
02     .exec(http("request_0")
03         .get("/")
04         .headers(headers_0)
05         .resources(http("request_1")
06         .get(uri2 + "/resources/sites/phoenix/style/font/teleneo-variable.woff2")
07         .headers(headers_1),
08          http("request_2")
09         .get(uri2 + "/binaries/assets/fonts/TeleGroteskScreen-Regular.woff")
10         .headers(headers_2),
11          http("request_3")
12         .get(uri2 + "/binaries/assets/fonts/phx-core-icons.woff")
13         .headers(headers_2),

The proxy recorder is a convenient way to create a sample script that can be used as a source when writing your own test scripts. This recorder can be run for all of the steps in your test. Then, you have all the headers as well as the URLs you will need in code form. However, all but the most simple tests should probably be refactored into a few Scala objects and classes to make the code understandable and the test script maintainable.

Extending Scala and Java

Perhaps the major advantage of Gatling tests is that they are written as small programs that can be organized into smaller, easy-to-understand units, which also makes the code calling it much easier to understand. The most reasonable decomposition would be to extract the testing steps from Listing 1 and put them into their own object, similar to Listing 4.

Listing 4

Test Code in Separate Object

01 package com.mypackagename
03 import io.gatling.core.Predef._
04 import io.gatling.http.Predef._
06 object DemoSteps {
08     // headers for HTTP call
09     var headers = Map(
10       "Accept" -> "text/html",
11       "User-Agent" -> "LinuxMagazine/1.0.1",
12       "Accept-Encoding" -> "gzip, deflate",
13       "Connection" -> "keep-alive",
14       "DNT" -> "1")
16     var getSitePage =
17       exec(http("step_1_getpage")
18         .get("/")
19         .headers(headers)
20         .check(
22     var getGooglePage =
23       exec(http("step_1_getgoogle")
24         .get("")
25         .headers(headers)
26         .check(
27 }

The new DemoSteps object contains the headers and two steps, each having a meaningful name. This code is easy to understand and maintain and makes the scenario definition four very readable lines:

val scn = scenario("BookFlight")

If this test had many steps and a more meaningful object name, it would be possible for anyone to have a complete understanding of what the test does and how it is organized.

You can create simple methods within a Scala object that use plain old Java code and library calls:

def getnow() : String = {
   var calendar = Calendar.getInstance()
   var now = calendar.getTime() + "";

In this way, you can leverage all of your expertise and custom libraries in your tests.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus