Running Load-Test-as-Code Simulations with Gatling
Tags: devops, loadtesting, gatling, github
Published: 2022-11-21 08:12:07

One of the essential skills in _DevOps Engineering_ is Load Testing. Load testing is a type of performance test checking the upper limits of your system by subjecting it to _extreme load conditions_. 

This type of test goes beyond the traditional _functional_, _regression_, and _integration testing_. It safeguards your infrastructure by simulating actual production traffic in a lower environment (non-production), allowing you to tweak capacity or setup scaling thresholds in preparation for these scenarios.

## Why Gatling?

**[Gatling](https://gatling.io/)** is _load-test-as-code_. It is a load testing tool that provides the basic capabilities of simulating load test scenarios and is designed with DevOps and CI/CD in mind. Did I mention it is open-source? You can literally download sample code, add a few tests in the template, and perform load testing from your local machine within 10 minutes!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzpm4f01tit98q36079v.png)

Gatling uses the _scala programming language_ for its simulation code. It combines object-oriented and functional programming in one concise, high-level language. It is very easy to learn, and has a lot of resources available with sample code.

Gatling produces [wonderful reports](https://gatling.io/docs/gatling/reference/current/stats/reports/). Each simulation run creates a graphical report allowing you to analyze results better, drilling-down to scenario level metrics.

_If you are testing scenarios of user actions within a browser, and you are not fond of creating tests in scala, you can also use the [Gatling Recorder](https://gatling.io/docs/gatling/reference/current/http/recorder/). Just simulate the actions one time, export the HAR (HTTP Archive) file from the browser's developer tools, load it in the recorder, and it will magically create your scala code for you!_

## Gatling Code Basic Structure

To start your load test journey, download Gatling's open source tool from the [gatling website](https://gatling.io/open-source/). It will be packaged as a zip file, containing the basic structure shown below:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98x0vgvsz85cds0fdf3z.png)

**1. Configuration File**

Inside the `conf` directory, there should be a json formatted `gatling.conf` file. This file contains configurable settings for Gatling and its default values.

From this file, go to the directory section and remove the commented code for _simulations_, _resources_, and _results_.

```conf
directory {
      simulations = "../user-files/simulations"                     # If set, directory where simulation classes are located
      resources = "../user-files/resources"                       # If set, directory where resources, such as feeder files and request bodies, are located
      #reportsOnly = ""                     # If set, name of report folder to look for in order to generate its report
      #binaries = ""                        # If set, name of the folder where compiles classes are located: Defaults to GATLING_HOME/target.
      results = "results"                    # Name of the folder where all reports folder are located
    }
```
This will tell gatling to look for simulation files under the  `user-files/simulations` directory to compile during run, and put the results in the `results` directory once the load test completes.

**2. Resources**

**Resources** are a set of values that you feed to your simulation tests. You can set up a scenario and use values in any resource file for load tests where you are simulating different parameter values for a single scenario.  

I won't be showing this in detail, just know that it exists and what it's for.

**3. Simulations**

**Simulations** contain your load tests. It's commonly coded in scala, but it is now available with Java if you are more familiar with this language.

**4. Results**

**Results** points to the directory where the report files will be created after you run the load test. You can view sample reports [here](https://gatling.io/docs/gatling/reference/current/stats/reports/).

## Create Your First Simulation Code

Going back to the simulation file, let's try creating REST API load tests in **scala**. First, copy the code below to a `.scala` file under your `simulations` directory.

```scala
package loadtestpackage

import io.gatling.core.scenario.Simulation
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class LoadTestSimulation1 extends Simulation {

}
```

Next, create a variable to store some value mappings that you will use redundantly across multiple APIs that you're testing. Some common use cases for this part is for authorization headers or api keys.

```scala
var credentials = Iterator.continually( 
                                        Map(
                                          "authorization" -> ( "Basic <BASE64 user:password>" ),
                                          "apikey" -> ( "<apikey random value here>" ) )
                                           )
                                      )     
```
Then, create an http configuration and store it in a variable. You can also add common headers here such as `Accept` and `Content-Type`.

```scala
val httpConf = http.baseUrl("https://www.domain.com")
        .header("Accept", value="application/json")
        .header("content-type", value="application/json")
```
Add a `scenario` to your test. Notice here that you are feeding the scenario with the `credentials` variable. Then, you can use the mapping value within any part of the scenario. Here, I'm using the authorization header and replacing it with the mapped value in my credentials variable. I also defined `200` and `204` status codes as successful responses of this API. 

```scala
val scn1 = scenario("GetUsersAPI")
      .feed(credentials)
      .exec(http("GET Users API")
      .get("/user/12345")
      .header("requestid","GUIDHERE")
      .header("Authorization",StringBody("""${authorization}"""))
      .check(status.in(200, 204))
      )
```
Setup the **load test**. Here, I'm injecting 5 concurrent users constantly throughout a duration of 10 minutes using the protocol in my http configuration variable. Other injection options, both open and closed can also be simulated referencing the full guide [here](https://gatling.io/docs/gatling/reference/current/core/injection/). If you have multiple scenarios defined in the same file, you can run them simultaneously by separating the setup code with comma.

```scala
setUp(            
	scn1.inject((constantConcurrentUsers(5).during(10.minutes)))
    	.protocols(httpConf)
	)
```

## Running Your Simulation Code

To run your simulation code, in your terminal, change directory to your `bin` folder. It is important to do this step to be able to view compile errors, if any. Run the `gatling.sh` script from this directory. 

If you have multiple simulation files in your simulation directory, all of those will be separately packaged during compilation and you will be asked to select which of the simulations you wish to run.

You can add a description (i.e. `Load Test Run 1`) to identify the simulation run and distinguish it from other runs. This description will be part of the created report once the simulation run is completed.

During the run, the screen will refresh every few seconds, showing the current load test status until it completes.

```shell
$./gatling.sh
GATLING_HOME is set to /Users/karina.alvarado/workspace/gatling-charts/highcharts-bundle-3.8.4
Choose a simulation number:
     [0] loadtestpackage.LoadTestSimulation1
     [1] loadtestpackage.LoadTestSimulation2
0
Select run description (optional)
Load Test Run 1
Simulation loadtestpackage.LoadTestSimulation1 started...

================================================================================
2022-11-11 01:00:00                                           5s elapsed
---- Requests ------------------------------------------------------------------
> Global                                                   (OK=10      KO=0    )
> GET Users API           			                       (OK=10      KO=0    )
---- Errors --------------------------------------------------------------------


---- GetUsersAPI ---------------------------------------------------------------
          active: 20     / done: 10   
================================================================================
```
## Results

If you have monitoring tools setup in your infrastructure such as Kibana, Splunk, SumoLogic, Prometheus, or other equivalent tools, you can always analyze your results to compare environments and how they differ in terms of performance against a heavy load.

If not, you can also use the report generated from the gatling test itself. A few screenshots on how the Gatling report looks like are shown below:

**Show Response Time Ranges of Each Scenario**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/944h8zjhg0flzxpqu4cb.png)

**Standard Statistics per Request**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h422ijn9keep118cgxl0.png)

**Active Users over Time**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qzkfqi42yogdg06vormk.png)

**Responses per Second over Time**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kunf2o9vk6a0qjrn5j3a.png)

There's a few more graphs shown in Gatling that may be helpful  depending on what you are trying to find out. Based on these results, you'll have some **data** to confirm whether the infrastructure that you're setting up needs to be tweaked to better prepare for real traffic once it's available in production.


---
**References:**
https://gatling.io/docs/gatling/reference/current/stats/reports/

https://gatling.io/docs/gatling/reference/current/http/recorder/