Gatling

Gatling Scripting – Understanding of Code

Image result for gatling tool coding

Gatling is an open-source load testing framework based on Scala, Akka and Netty. It comes with excellent support for HTTP protocol thus making it an easy choice to test HTTP server (web based applications, APIs etc)..Build on top of Akka it enables thousands of virtual users on a single machine. Akka has message-driven architecture and this overrides the JVM limitation of handling many threads. Virtual users are not threads but messages. Tests are written in Scala.

This blog post will explain how to enhance/update the Gatling scripts in Scala programming language .

A script in Gatling is divided into below parts:

  • Define common HTTP protocol configuration
  • Define Headers
  • Define Feeders
  • Define HTTP Requests
  • Define scenario 
  • Load Injection

A Gatling simulation is a real Scala class in which you define almost everything required to run a load test i.e. input data, scenario configuration, requests, load injection pattern etc.
A test in Gatling is called a simulation. Extending Simulation class will give us access to tools needed to set up and execute tests. The Scala Script/class contains following parts:

Let’s go through each step in detail:

Step 1: Define common HTTP protocol configuration

It is defined as a Scala variable so that it can be passed later in the simulation definition. Following definition shows various parameters which can be used while defining the protocol configuration with baseURL being the core parameter.

  1. This baseURL will be prepended to all URLs that do not start with HTTP
  2. Same as 1 but load testing several servers with client based load balancing. The selection of the URL is random.
  3. Using inGatling will automatically parse HTML to find embedded resources and load them asynchronously. To emulate the behavior of real web browser
  4. Various headers
  5. Gatling automatically performs a request to http://gatling.io before starting the actual simulation. This is used to nullify the overhead introduced by java/NIO engine on the first request. Use this to disable the feature
  6. OR use your own warm-up URL instead of default one
  7. Gatling can run multiple concurrent connections per virtual user when fetching resources on the same host. The default value is 6. Use this parameter to use the built-in value for chrome browser. There are many other built-in values for different browser types, see here
  8. Request stats are logged and then used to produce reports. Sometimes, some requests may be important for you for generating load, but you don’t actually want to report them. So we silence them by using this. Silent requests won’t be reported and won’t influence error triggers such as tryMax and exitHereIfFailed. Yet, response times will be accounted for in group times.
  9. OR use this. Same as silentResources but silences only matching requests as per regular expression
  10. To disable automatic redirects (happens by default) in case of HTTP 301/ 302/303/307 response status code for the original request
  11. used to set the maximum number of redirects. To avoid infinite redirection loops
  12. Used to disable caching. When a response gets cached, checks are disabled.

Step 2: Define Headers

Gatling allows you to use common headers at HTTP protocol level (as mentioned in step 1 comment 4 above). You can also make use of the build in headers to pass to individual requests. Here are some of the header definitions. We will see in subsequent steps on how they can be used in the requests:

Step 3: Define Feeders

Feeders resemble parameter files in Loadrunner and Jmeter. As the name suggests, these are used to feed data to your simulation. Feeder is a type alias for Iterator[Map[String, T]], meaning that the component created by the feed method will poll  Map[String, T] records and inject its content. Gatling provides various built-in feeders like csv feederjson feederJDBC feedersitemap feederredis feeder etc. 

  1. Required import for UUID
  2. Required import for Base64
  3. Creates a requestId feeder of random UUID – returns a randomUUID each time a call is made to this feeder
  4. Creates someId feeder which returns a base64 encoded random alphanumeric string of length 16.
  5. Creates a credential_feeder for user name and password from a CSV file.
  6. Passing the credential_feeder feeder in transaction.

Step 4: Define HTTP Requests

Now let’s define HTTP requests which will be simulated in the test scenario. Consider following sample request defined as a Scala variable. It takes a feeder as input, executes qautoform with think time between 2 and 3 second.

Step 5: Define scenario 

A scenario can be defined as a Scala variable or Scala function. The following scenario is defined as a Scala function which takes a step, pacing, and duration as arguments.

In above example we have divided the scripts into different modules like ,launch ,login QuickFindSearch etc and 3 sections Init ,Action and End similar to Load Runner.

Step 6: Load Injection

The definition of the injection profile of users is done with the inject method. This method takes as argument a sequence of injection steps that will be processed sequentially. A simulation can contain only one setUp [/code] method, the setUpmethod can have multiple inject profiles in it.

Load testing with 10 concurrent users

Load testing with 10 users added over 20 seconds (1 extra user every 2 s)

Executing different scenarios in parallel with same protocol.

Executing different scenarios in parallel with different protocol.

Executing parametrized scenario

  1. Execute ApiLoad scenario defined in step 5 passing step=1, pacing=pPacing, and duration=pTestDuration as arguments. Scenario name will become “API Scenario 1” as the value of step is 1. See the second example to know why I used this.
  2. In this injection pattern, nothing is run for 10 seconds
  3. then pVusers are ramped up over pRampUpTime seconds. Once all pVusers are ramped up, users keep on injecting the load as defined in the scenario for duration seconds.
  4. Use protocol configs defined in step 1 in this post
  5. Assertions are used to verify global statistics like response time or the number of failed requests matches expectations for a whole simulation. All the assertions are evaluated after running the simulation. If at least one assertion fails, the simulation is reported as failed.
  6. maxDuration – use this only when you want to put a hard limit on simulation execution duration because this overrides the combined time of pRampUpTime, pPacing, pause, pTestDuration used in the scenario setup and inject method etc.

Stress or Staircase load pattern

This scenario is similar to above except, that the load is increased by pVusers at regular intervals of pStepTime for pNumSteps steps. i.e. staircase like load pattern – useful for stress or breakpoint test:

You see, three unique scenarios with names “API Scenario 1“, “API Scenario 2“, and “API Scenario 3“. The staircase-like load pattern visualized:

loadpattern

before and after hooks

we can define before and after hooks, both are optional in a simulation. The before hook executes before a simulation starts. The after hook executes after a simulation ends. Hooks are useful when you want to resume or update a sequence counter where a previous test ended/started. For example, in the following before hook, we are setting few values in the simulation using a properties file.

Three different ways for Gatling script structure:

Gatling Script Structure 1 with Multiple scenarios and groups .Scenario creation by chain of multiple groups. It is like Init ,Action and End section like Load Runner . Multiple requests are grouped to form a transaction.

//Define an optional package to hold the Scala class
package com.qautomation.performance
 
//required imports
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
 
//class declaration
class BasicSimulation extends Simulation {
//Step1: Define Common HTTP protocol configuration
  val uri1 = "https://qautomation.blog/Test/api"
  val httpProtocol = http
		.baseURL("https://qautomation.blog")
		.acceptHeader("application/json, text/plain, */*")
		.acceptEncodingHeader("gzip, deflate")
		.acceptLanguageHeader("en-US,en;q=0.9")
		.userAgentHeader("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36")

//Step2: Define Headers
val headers_0 = Map(
		"Content-Type" -> "application/json;charset=UTF-8",
		"Origin" -> "https://qautomation.blog",
		"PreferredLanguage" -> "en-us")

val headers_1 = Map("PreferredLanguage" -> "en-us")
//Step3: Define Feeders
val credential_feeder = csv("UserCredentials.csv").circular
//Step4: Define HTTP Requests
val Transaction_Name_1 = group("Transaction_1")
						{
						 exec(http("Http_Request_Name_1")
							.get("/Test1/httpRequestName1")
							.headers(headers_0))
							.pause(2, 3)
							.feed(credential_feeder)	
							
						}
val Transaction_Name_2 = group("Transaction_2")
						{
							exec(http("Http_Request_Name_2")
								.post("/Test2/httpRequestName2")
								.headers(headers_0)
								.body(StringBody("""{"Username":"${UserName}","Password":"${Password}"}""")))
							.pause(516 milliseconds)
							.exec(http("Http_Request_Name_3")
								.get("/Test3/httpRequestName3")
								.headers(headers_1)
						}
val Transaction_Name_3 = group("Transaction_3")
						{
							exec(http("Http_Request_Name_4")
								.delete("/Test4/httpRequestName4")
								.headers(headers_0))
							.exec(flushCookieJar) //Flush Cookie
							.exec(flushHttpCache) //Flush Cahce
							.exec { session =>
									println(session)
									session
								  }
						}

//Step5: Define Scenario
val Init   = exec(Transaction_Name_1)
val Action = exec(Transaction_Name_2,Transaction_Name_3,........Transaction_Name_n-1)
val End    = exec(Transaction_Name_n)
	
val scn = scenario("Load_Test_Scenario").exec(exec(Init).during(300 seconds)
		 {
			pace(10)
			exec(Action)
		 }
		 )
		 .exec(End)
//Step6: Load Injection pattern
	setUp(scn.inject(atOnceUsers(1))).protocols(httpProtocol)

}

Gatling Script Structure 2 with single scenario containing multiple groups .

//Define an optional package to hold the Scala class
package com.qautomation.performance
 
//required imports
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
 
//class declaration
class BasicSimulation extends Simulation {
//Step1: Define Common HTTP protocol configuration
  val uri1 = "https://qautomation.blog/Test/api"
  val httpProtocol = http
		.baseURL("https://qautomation.blog")
		.acceptHeader("application/json, text/plain, */*")
		.acceptEncodingHeader("gzip, deflate")
		.acceptLanguageHeader("en-US,en;q=0.9")
		.userAgentHeader("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36")

//Step2: Define Headers
val headers_0 = Map(
		"Content-Type" -> "application/json;charset=UTF-8",
		"Origin" -> "https://qautomation.blog",
		"PreferredLanguage" -> "en-us")

val headers_1 = Map("PreferredLanguage" -> "en-us")
//Step3: Define Feeders
val token_feeder = csv("UserCredentials.csv").circular
//Step4: Define HTTP Requests & Define Scenario

val scnwithDuration = scenario("Scenario_Load_Test").during(20 seconds)
	{
	
		pace(5 seconds)
		group("Transaction_1")
		{
		 exec(http("Transaction_Name_1")
			.get("/Test1/HttpTransactionName1")
			.headers(headers_0))
			.pause(2, 3)
			.feed(token_feeder)
		}
		.group("Transaction_2")
		{		  
		  exec(http("Transaction_Name_2")
			.post("/Test2/HttpTransactionName2")
			.headers(headers_1)			
			.body(StringBody("""{"Username":"${UserName}","Password":"${Password}"}""")))
			.pause(2, 3)
		}
		.pause(1)
		.group("Transaction_2")
		{
		 exec(http("Transaction_Name_3")
			.delete("/Test3/HttpTransactionName3")
			.headers(headers_0))	
		 .exec { session =>
				println(session)
				session
					}	
		
	}		

//Step6: Load Injection pattern
setUp(scnwithDuration.inject(rampUsers(1) over(2 seconds))).protocols(httpProtocol)

}

Gatling Script Structure 2 with single scenario created by the chain of multiple groups .

//Define an optional package to hold the Scala class
package com.qautomation.performance
 
//required imports
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
 
//class declaration
class BasicSimulation extends Simulation {
//Step1: Define Common HTTP protocol configuration
  val uri1 = "https://qautomation.blog/Test/api"
  val httpProtocol = http
		.baseURL("https://qautomation.blog")
		.acceptHeader("application/json, text/plain, */*")
		.acceptEncodingHeader("gzip, deflate")
		.acceptLanguageHeader("en-US,en;q=0.9")
		.userAgentHeader("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36")

//Step2: Define Headers
val headers_0 = Map(
		"Content-Type" -> "application/json;charset=UTF-8",
		"Origin" -> "https://qautomation.blog",
		"PreferredLanguage" -> "en-us")

val headers_1 = Map("PreferredLanguage" -> "en-us")
//Step3: Define Feeders
val token_feeder = csv("UserCredentials.csv").circular
//Step4: Define HTTP Requests 
val Transaction_Name_1 = group("Transaction_1")
						{
						 exec(http("Http_Request_Name_1")
							.get("/Test1/httpRequestName1")
							.headers(headers_0))
							.pause(2, 3)
							.feed(credential_feeder)	
							
						}
val Transaction_Name_2 = group("Transaction_2")
						{
							exec(http("Http_Request_Name_2")
								.post("/Test2/httpRequestName2")
								.headers(headers_0)
								.body(StringBody("""{"Username":"${UserName}","Password":"${Password}"}""")))
							.pause(516 milliseconds)
							.exec(http("Http_Request_Name_3")
								.get("/Test3/httpRequestName3")
								.headers(headers_1)
						}
val Transaction_Name_3 = group("Transaction_3")
						{
							exec(http("Http_Request_Name_4")
								.delete("/Test4/httpRequestName4")
								.headers(headers_0))
							.exec(flushCookieJar) //Flush Cookie
							.exec(flushHttpCache) //Flush Cahce
							.exec { session =>
									println(session)
									session
								  }
						}	

//Step5: Define Scenario

val scn = scenario("Scenario_With_chain").during(300 seconds)
	{
		exec(Transaction_Name_1,Transaction_Name_2,Transaction_Name_3)
	}

//Step6: Load Injection pattern 
setUp(scn.inject(atOnceUsers(5))).protocols(httpProtocol)
}

Done! We’ve gone through various ways of creating scenarios and simulations.

References:

https://gatling.io/docs/2.3/quickstart/

https://automationrhapsody.com/performance-testing-with-gatling-recorded-simulation-explanation/

Categories: Gatling

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s