Loadtest REST API with gatling opensource
Tags: loadtest, gatling, api, scala
Published: 2020-01-16 12:09:56

---
title: Loadtest REST API with gatling opensource
published: true
description: 
tags: #loadtest #gatling #api #scala
---

Hi folks, today I'm gonna show you how to loadtest your rest api with gatling tool.

I.Why loadtest:

 Load testing gives confidence in the system & its reliability and performance. Load Testing helps identify the bottlenecks in the system under heavy user stress scenarios before they happen in a production environment.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/ldoab84fiw9m5pwlxo7d.jpg)

You can find out more about loadtest in this [guru99](https://www.guru99.com/load-testing-tutorial.html)

II.What is gatling:

Gatling is a loadtest tool created based on Scala, Akka and Netty.

You can findout more information about gatling in [official gatling site](https://gatling.io/)

Gatling provides the DSL (Domain specific language) for us to create the scenario and setup for loadtesting.

Below is the code snippet for a gatling simulation scenario.

```javascript
class ConcurrentRequests extends BaseSimulation{

  val scn: ScenarioBuilder = scenario("Check concurrentRequests for get song detail api")
    .exec(
      http("Get song detail api")
        .get(GeneralConfigs.COCCOCMUSIC_MOBILE_API_SONGS+"/"
          +EnvironmentConfigs.COCCOCMUSIC_MOBILE_API_TESTDATA_SONGDETAIL.zingmp3
        +"_"+"audio")
        .check(status.is(200)))

  setUp(scn.inject(nothingFor(4),atOnceUsers(1),rampUsersPerSec(1) to (2) during(10)).protocols(mobileApiProtocol)).maxDuration(100)
    .assertions(global.responseTime.max.lt(2000), global.successfulRequests.percent.gt(95))

}

```

Or you can use the gatling frontline version (commercial version) in [here](https://gatling.io/gatling-frontline/)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/mdywwaisoc7i9n8tiisv.jpg)

III.What is Scala:

Scala stands for scalable language which compile to java byte code.
Scala is a multiparadigm language that supports both object-oriented and functional programming style.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/q74ywdrwtoaq053b923x.png)

You can find out more about Scala in [here](https://www.scala-lang.org/)

IV.Set things up:

 1.IntelliJ:

For our loadtest project, we will need to use intelliJ (a product by Jetbrains) which targets supporting JVM languages.

You can download the IntelliJ from [here](https://www.jetbrains.com/idea/)

 2.Maven:

We will use maven as a build tool for the project, so you will need to install maven.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/tgtx6f8cfkwmyirilk6f.png)

Please download maven from [here](https://maven.apache.org/download.cgi)

After download, please set the system path to the bin directory of maven

 3.Java jdk:

Please download jdk8 for stable version working with Scala and Gatling.

You can download from [here](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)

 4.Scala sdk:

You can set the scala sdk directly in IntelliJ.
IntelliJ will automatically found the scala sdk version for you and suggest if you have not done so.

The Scala sdk version you should install is 2.12.10 for stability

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/lx08dhhmmfq26h1xz2ov.PNG)

V.Get your hands dirty in the real project:

1.Create a maven scala project in IntelliJ:

Go to File -> New project -> tick on create from archetype -> choose scala-archetype-simple

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/qw4isx9ct8u7wr4ejtug.PNG)

2.Pom file:

We will add dependencies for our project in the pom file.

Also, we add the mechanism so that for each profile we parse into maven commandline, it will choose the correct env config file for us.

The pom file would be like below, a little bit long (:D)

```javascript
#pom file

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>coccoc-music</groupId>
  <artifactId>mobile-api-perf</artifactId>
  <version>1.0-SNAPSHOT</version>
  <inceptionYear>2008</inceptionYear>
  <properties>
    <scala.version>2.12.10</scala.version>
    <gatling.version>3.3.1</gatling.version>
  </properties>

  <repositories>
    <repository>
      <id>scala-tools.org</id>
      <name>Scala-Tools Maven2 Repository</name>
      <url>http://scala-tools.org/repo-releases</url>
    </repository>
  </repositories>

  <dependencies>
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>${scala.version}</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.4</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.specs</groupId>
      <artifactId>specs</artifactId>
      <version>1.2.5</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.gatling</groupId>
      <artifactId>gatling-test-framework</artifactId>
      <version>3.3.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.gatling/gatling-core -->
    <dependency>
      <groupId>io.gatling</groupId>
      <artifactId>gatling-core</artifactId>
      <version>3.3.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.gatling.highcharts/gatling-highcharts -->
    <dependency>
      <groupId>io.gatling.highcharts</groupId>
      <artifactId>gatling-highcharts</artifactId>
      <version>3.3.1</version>
      <type>pom</type>
    </dependency>
    <!-- https://mvnrepository.com/artifact/io.gatling.highcharts/gatling-charts-highcharts -->
    <dependency>
      <groupId>io.gatling.highcharts</groupId>
      <artifactId>gatling-charts-highcharts</artifactId>
      <version>3.3.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.typesafe.play/play-json -->
    <dependency>
      <groupId>com.typesafe.play</groupId>
      <artifactId>play-json_2.12</artifactId>
      <version>2.8.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.maven.plugins/maven-antrun-plugin -->
    <dependency>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-antrun-plugin</artifactId>
      <version>1.8</version>
    </dependency>
  </dependencies>


  <build>
    <testSourceDirectory>src/test/scala</testSourceDirectory>
    <plugins>
      <plugin>
        <groupId>io.gatling</groupId>
        <artifactId>gatling-maven-plugin</artifactId>
        <version>3.0.5</version>
      </plugin>
    </plugins>
  </build>

  <profiles>
    <profile>
      <id>dev</id>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
              <execution>
                <phase>clean</phase>
                <goals>
                  <goal>run</goal>
                </goals>
                <configuration>
                  <tasks>
                    <!-- copy file db of profile and iWealth for test environment -->
                    <delete file="src/main/resources/env.json"/>
                    <copy file="src/main/resources/env.dev.json"
                          tofile="src/main/resources/env.json"/>
                  </tasks>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
    <profile>
      <id>stag</id>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
              <execution>
                <phase>clean</phase>
                <goals>
                  <goal>run</goal>
                </goals>
                <configuration>
                  <tasks>
                    <!-- copy file db of profile and iWealth for test environment -->
                    <delete file="src/main/resources/env.json"/>
                    <copy file="src/main/resources/env.stag.json"
                          tofile="src/main/resources/env.json"/>
                  </tasks>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
    <profile>
      <id>prd</id>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-antrun-plugin</artifactId>
            <executions>
              <execution>
                <phase>clean</phase>
                <goals>
                  <goal>run</goal>
                </goals>
                <configuration>
                  <tasks>
                    <!-- copy file db of profile and iWealth for test environment -->
                    <delete file="src/main/resources/env.json"/>
                    <copy file="src/main/resources/env.prd.json"
                          tofile="src/main/resources/env.json"/>
                  </tasks>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>

</project>


```

3.Project structure:

#main package:

We only define the resources file in the main package

The tree is like:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/ndzjukpr2ia5salnxcyn.PNG)

File env.json is the file where we store real env usage for current run
env.{env_name}.json is the files to store env for each environment
general.json is the file to store general config value

#test package:

We will have the below package:

- configs
To store configuration files to get value from config file in resources folder above

For example : The code below demonstrate how to store config value to the variable and use that in our test script

```javascript

package coccocMusic.configs
import coccocMusic.models.{Environments, PlaylistDetail, SongDetail}
import utils.FileUtilsInternal
import play.api.libs.json.Json


object EnvironmentConfigs {

  val fileUtilsInternal = new FileUtilsInternal

  def environment():Environments= {

    val jsonEnvironments = fileUtilsInternal.parseFileToJson(System.getProperty("user.dir") + "\\src\\main\\resources\\env.json")
    val environmentsValue = Json.fromJson[Environments](jsonEnvironments)
    environmentsValue.getOrElse(Environments).asInstanceOf[Environments]
  }

  final val COCCOCMUSIC_MOBILE_API_DOMAIN: String = environment().coccocMusic.mobile.api.domain
  final val COCCOCMUSIC_MOBILE_API_BASICAUTH_USERNAME: String = environment().coccocMusic.mobile.api.basicAuth.username
  final val COCCOCMUSIC_MOBILE_API_BASICAUTH_PASSWORD: String = environment().coccocMusic.mobile.api.basicAuth.password
  final val COCCOCMUSIC_MOBILE_API_TESTDATA_PLAYLISTDETAIL: PlaylistDetail = environment().coccocMusic.mobile.api.testData.playlistDetail
  final val COCCOCMUSIC_MOBILE_API_TESTDATA_SONGDETAIL: SongDetail = environment().coccocMusic.mobile.api.testData.songDetail
  final val COCCOCMUSIC_MOBILE_API_TESTDATA_PLAYLISTSBYCATEGORY: Int = environment().coccocMusic.mobile.api.testData.playlistsByCategory

}


```

- executions: to store our test scenario

for example the scenario for test our home screen api:

```javascript

package coccocMusic.executions.homeScreen.scenarios

import coccocMusic.configs.GeneralConfigs
import coccocMusic.setup.BaseSimulation
import io.gatling.core.Predef._
import io.gatling.core.structure.ScenarioBuilder
import io.gatling.http.Predef._

class ConcurrentRequests extends BaseSimulation {

  val scn: ScenarioBuilder = scenario("Check concurrentRequests for homescreen api")
    .exec(
      http("Get playlists and songs")
        .get(GeneralConfigs.COCCOCMUSIC_MOBILE_API_HOMESCREEN)
    .check(status.is(200)))

  val fromUsers: Double = Integer.getInteger("fromUsers",1).toDouble
  val toUsers: Double = Integer.getInteger("toUsers",1).toDouble
  val duringSeconds: Integer = Integer.getInteger("duringSeconds",1)

  setUp(scn.inject(nothingFor(4),atOnceUsers(1),rampUsersPerSec(fromUsers) to (toUsers) during(duringSeconds)).protocols(mobileApiProtocol)).maxDuration(100)
    .assertions(global.responseTime.max.lt(2000), global.successfulRequests.percent.gt(95))

}


```

Above we define the endpoint for our api : GeneralConfigs.COCCOCMUSIC_MOBILE_API_HOMESCREEN

Get the input fromUsers, toUsers, duringSeconds from commandline, auto set to 1 if not defined (-DfromUsers={value} -DtoUsers={value} -DduringSeconds={value}

- models:
To define models for the environments and general configurations (Remember we got json file for resources, so models make total sense)

For example like below:

```javascript
package coccocMusic.models
import play.api.libs.functional.syntax._
import play.api.libs.json.{JsPath, Reads}
import play.api.libs.json._

case class Environments(coccocMusic: CoccocMusic)

case class CoccocMusic(mobile: Mobile)

case class Mobile(api: Api)

case class Api(domain: String, basicAuth: BasicAuth, testData: TestData)

case class BasicAuth(username: String, password: String)

case class TestData(playlistDetail: PlaylistDetail, songDetail: SongDetail, playlistsByCategory: Int)

case class PlaylistDetail(nhaccuatui: String, zingmp3: String)

case class SongDetail(nhaccuatui: String, zingmp3: String)

object BasicAuth{
  implicit val basicAuthReads: Reads[BasicAuth] = (
    (JsPath \ "username").read[String] and
      (JsPath \ "password").read[String]
    )(BasicAuth.apply _)
}

object TestData{
  implicit val testDataReads: Reads[TestData] = (
    (JsPath \ "playlistDetail").read[PlaylistDetail] and
      (JsPath \ "songDetail").read[SongDetail] and
      (JsPath \ "playlistsByCategory").read[Int]
  )(TestData.apply _)
}

object PlaylistDetail{
  implicit val playlistDetailReads: Reads[PlaylistDetail] = (
    (JsPath \ "nhaccuatui").read[String] and
      (JsPath \ "zingmp3").read[String]
  )(PlaylistDetail.apply _)
}

object SongDetail{
  implicit val songDetailReads: Reads[SongDetail] =(
    (JsPath \ "nhaccuatui").read[String] and
      (JsPath \ "zingmp3").read[String]
  )(SongDetail.apply _)
}

object Api{
  implicit val apiReads: Reads[Api] = (
    (JsPath \ "domain").read[String] and
      (JsPath \ "basicAuth").read[BasicAuth] and
      (JsPath \ "testData").read[TestData]
    )(Api.apply _)
}

object Mobile{

  implicit val mobileReads: Reads[Mobile] =
    (__ \ "api").read[Api].map(Mobile.apply)
}

object CoccocMusic{

  implicit val coccocMusicReads: Reads[CoccocMusic] =
    (__ \ "mobile").read[Mobile].map(CoccocMusic.apply)
}

object Environments{
  implicit val coccocMusicReads: Reads[Environments] =
    (__ \ "coccocMusic").read[CoccocMusic].map(Environments.apply)
}


```

- setup :

We will set the BaseSimulation class for after use.

Example code below for setting the domain and basic auth:

```javascript

package coccocMusic.setup
import coccocMusic.configs.{EnvironmentConfigs, GeneralConfigs}
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import io.gatling.http.protocol.HttpProtocolBuilder

class BaseSimulation extends Simulation{


  val mobileApiProtocol: HttpProtocolBuilder = http.baseUrl(EnvironmentConfigs.COCCOCMUSIC_MOBILE_API_DOMAIN
    +GeneralConfigs.COCCOCMUSIC_MOBILE_API_PREFIX+GeneralConfigs.COCCOCMUSIC_MOBILE_API_VERSION).basicAuth(
    EnvironmentConfigs.COCCOCMUSIC_MOBILE_API_BASICAUTH_USERNAME,EnvironmentConfigs.COCCOCMUSIC_MOBILE_API_BASICAUTH_PASSWORD)


}


```

- utils:
This is where we define the utility we make for our project

Example for the utils to parse the json file to json object:

```javascript

package utils

import play.api.libs.json.{JsValue, Json}

import scala.io.Source

class FileUtilsInternal {

  def parseFileToJson(pathName: String): JsValue = {

    val bufferSource=Source.fromFile(pathName)
    var jsonFormat: JsValue = null
    try {
      val source:String=bufferSource.getLines.mkString
      jsonFormat = Json.parse(source)
  }
    finally {
      bufferSource.close()
    }
    jsonFormat
  }

}


```

4. Run the scenario:

For example we can run the scenario for home screen api like below:

mvn clean gatling:test -Pdev -Dgatling.simulationClass=coccocMusic.executions.homeScreen.scenarios.ConcurrentRequests -DfromUsers=10 -DtoUsers=14 -DduringSeconds=20

Let me explain in detail

mvn clean : to clean the target folder

gatling:test to run the test

-Pdev : this is to remove the content in env.json file, and parse the content from env.dev.json to env.json (You can definitely change to -Pstag or -Pprd for different environment)

-Dgatling.simulationClass=coccocMusic.executions.homeScreen.scenarios.ConcurrentRequests : this to specify the simulationClass to run the test

-DfromUsers=10 -DtoUsers=14 -DduringSeconds=20 : this is to define the number of users to users, the duration for ramping up the requests

5. The report file:

Will look like this where you can see the details for overall or for specific request (located in target/gatling/scenario_name/index.html)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/326yrcs9czwqq1nb0lrq.png)

VI.Source code:

 The source code for the project you can find [here](https://github.com/cuongld2/coccocApiPerf) in github

That's it. I hope it helps

Peace as always!!!


Notes: If you feel this blog help you and want to show the appreciation, feel free to drop by :

[<img src="https://thepracticaldev.s3.amazonaws.com/i/cno42wb8aik6o9ek1f89.png">](https://www.buymeacoffee.com/dOaeSPv
)

This will help me to contributing more valued contents.


Happy coding!!!