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.
You can find out more about loadtest in thisguru99
II.What is gatling:
Gatling is a loadtest tool created based on Scala, Akka and Netty.
You can findout more information about gatling inofficial gatling site
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.
classConcurrentRequestsextendsBaseSimulation{valscn: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) inhere
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.
You can find out more about Scala inhere
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 fromhere
2.Maven:
We will use maven as a build tool for the project, so you will need to install maven.
Please download maven fromhere
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 fromhere
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
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
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)
#pomfile<projectxmlns="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-ToolsMaven2Repository</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><!--copyfiledbofprofileandiWealthfortestenvironment--><deletefile="src/main/resources/env.json"/><copyfile="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><!--copyfiledbofprofileandiWealthfortestenvironment--><deletefile="src/main/resources/env.json"/><copyfile="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><!--copyfiledbofprofileandiWealthfortestenvironment--><deletefile="src/main/resources/env.json"/><copyfile="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:
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:
- configsTo 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
packagecoccocMusic.configsimportcoccocMusic.models.{Environments,PlaylistDetail,SongDetail}importutils.FileUtilsInternalimportplay.api.libs.json.JsonobjectEnvironmentConfigs{valfileUtilsInternal=newFileUtilsInternaldefenvironment():Environments={valjsonEnvironments=fileUtilsInternal.parseFileToJson(System.getProperty("user.dir")+"\\src\\main\\resources\\env.json")valenvironmentsValue=Json.fromJson[Environments](jsonEnvironments)environmentsValue.getOrElse(Environments).asInstanceOf[Environments]}finalvalCOCCOCMUSIC_MOBILE_API_DOMAIN:String=environment().coccocMusic.mobile.api.domainfinalvalCOCCOCMUSIC_MOBILE_API_BASICAUTH_USERNAME:String=environment().coccocMusic.mobile.api.basicAuth.usernamefinalvalCOCCOCMUSIC_MOBILE_API_BASICAUTH_PASSWORD:String=environment().coccocMusic.mobile.api.basicAuth.passwordfinalvalCOCCOCMUSIC_MOBILE_API_TESTDATA_PLAYLISTDETAIL:PlaylistDetail=environment().coccocMusic.mobile.api.testData.playlistDetailfinalvalCOCCOCMUSIC_MOBILE_API_TESTDATA_SONGDETAIL:SongDetail=environment().coccocMusic.mobile.api.testData.songDetailfinalvalCOCCOCMUSIC_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:
packagecoccocMusic.executions.homeScreen.scenariosimportcoccocMusic.configs.GeneralConfigsimportcoccocMusic.setup.BaseSimulationimportio.gatling.core.Predef._importio.gatling.core.structure.ScenarioBuilderimportio.gatling.http.Predef._classConcurrentRequestsextendsBaseSimulation{valscn:ScenarioBuilder=scenario("Check concurrentRequests for homescreen api").exec(http("Get playlists and songs").get(GeneralConfigs.COCCOCMUSIC_MOBILE_API_HOMESCREEN).check(status.is(200)))valfromUsers:Double=Integer.getInteger("fromUsers",1).toDoublevaltoUsers:Double=Integer.getInteger("toUsers",1).toDoublevalduringSeconds: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:
packagecoccocMusic.modelsimportplay.api.libs.functional.syntax._importplay.api.libs.json.{JsPath,Reads}importplay.api.libs.json._caseclassEnvironments(coccocMusic:CoccocMusic)caseclassCoccocMusic(mobile:Mobile)caseclassMobile(api:Api)caseclassApi(domain:String,basicAuth:BasicAuth,testData:TestData)caseclassBasicAuth(username:String,password:String)caseclassTestData(playlistDetail:PlaylistDetail,songDetail:SongDetail,playlistsByCategory:Int)caseclassPlaylistDetail(nhaccuatui:String,zingmp3:String)caseclassSongDetail(nhaccuatui:String,zingmp3:String)objectBasicAuth{implicitvalbasicAuthReads:Reads[BasicAuth]=((JsPath\"username").read[String]and(JsPath\"password").read[String])(BasicAuth.apply_)}objectTestData{implicitvaltestDataReads:Reads[TestData]=((JsPath\"playlistDetail").read[PlaylistDetail]and(JsPath\"songDetail").read[SongDetail]and(JsPath\"playlistsByCategory").read[Int])(TestData.apply_)}objectPlaylistDetail{implicitvalplaylistDetailReads:Reads[PlaylistDetail]=((JsPath\"nhaccuatui").read[String]and(JsPath\"zingmp3").read[String])(PlaylistDetail.apply_)}objectSongDetail{implicitvalsongDetailReads:Reads[SongDetail]=((JsPath\"nhaccuatui").read[String]and(JsPath\"zingmp3").read[String])(SongDetail.apply_)}objectApi{implicitvalapiReads:Reads[Api]=((JsPath\"domain").read[String]and(JsPath\"basicAuth").read[BasicAuth]and(JsPath\"testData").read[TestData])(Api.apply_)}objectMobile{implicitvalmobileReads:Reads[Mobile]=(__\"api").read[Api].map(Mobile.apply)}objectCoccocMusic{implicitvalcoccocMusicReads:Reads[CoccocMusic]=(__\"mobile").read[Mobile].map(CoccocMusic.apply)}objectEnvironments{implicitvalcoccocMusicReads: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:
packagecoccocMusic.setupimportcoccocMusic.configs.{EnvironmentConfigs,GeneralConfigs}importio.gatling.core.Predef._importio.gatling.http.Predef._importio.gatling.http.protocol.HttpProtocolBuilderclassBaseSimulationextendsSimulation{valmobileApiProtocol: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:
packageutilsimportplay.api.libs.json.{JsValue,Json}importscala.io.SourceclassFileUtilsInternal{defparseFileToJson(pathName:String):JsValue={valbufferSource=Source.fromFile(pathName)varjsonFormat:JsValue=nulltry{valsource:String=bufferSource.getLines.mkStringjsonFormat=Json.parse(source)}finally{bufferSource.close()}jsonFormat}}
- 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
- 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)
VI.Source code:
The source code for the project you can findhere 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 :
This will help me to contributing more valued contents.
Happy coding!!!
Top comments(1)
For further actions, you may consider blocking this person and/orreporting abuse