背景:最近在朋友圈,有不少人在使用Gatling性能测试工具,都说相比jmeter开源工具有不少优势,于是在IntelliJ IDEA开发工具中实践和体验了一番maven+Gatling的性能测试,实践如下:
1、搭建IntelliJ IDEA、scala、maven、Gatling环境,请自行百度。但需注意2点
(1)、IntelliJ IDEA的maven项目中新建scala class时,无法显示出来,参考地址设置:
https://blog.csdn.net/program_anywhere/article/details/53728186
(2)、JDK版本要1.8版本以上
2、参数化数据Test_data.csv文件存放的位置,如图:
3、新建testcase.scala文件,编写脚本如下:
(注:1、新增scala class 必须继承Simulation ,否则无法执行 ;2、有人注意到,为啥会有多个URL地址,是因为我们系统采用的是前后端分离,前端地址和后台接口地址不一样;3、我采用了场景分离,可以随意定义执行那个,比较灵活 ;4、scala关于在打印响应值和获取关联参数日志,目前实践发现,在check后,保存到session指定参数中,至于原因,目前未了解不深,不做解释;5、脚本中未设计ssl设置,如有需要,可参考英文论坛地址实践:https://groups.google.com/forum/#!topic/gatling/iQUFQbFLbtA(有时需要跨域查看);6、关于feed参数化时,迭代方式一样要多实践,得到自己想要的;7、参数引用的方式“${参数名}”)
package basic
import io.gatling.core.scenario.Simulation
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class testcase01 extends Simulation{
private val public_url = "http://ip地址" //后台接口地址
/**发送home请求
* Get请求
* response返回值为html
*/
object Home{
val home = exec(http("Home_request") //设置请求名称,可随意定义
.get("http://ip地址") //前端请求地址
.check(status.is(200)) //判断http status
.check(bodyString.saveAs("Get_bodys")) //获取response中body所有值,并存入session中Get_bodys参数
.check(header("ETag").saveAs("Get_header")) //获取response的header中某个参数值,注:一般用于获取cookies的值
.check(regex("<meta http-equiv=content-type content=\"(.*)\">").saveAs("Get_parm")) //采用regex获取html中某个值
)
.pause(2) //设置思考时间2s
//修改session中信息
.exec(session =>{
val header_value = session("Get_header").as[String] //获取session中参数和对应值
val session_id = header_value.split("/")(1) //获取关联值处理
session.set("Get_session_id",session_id) //对session中添加一个新参数,并给新参数赋值
}
)
//打印session信息
.exec{ session => println(session)
session
}
}
/** 发送登录请求
* POST请求
* 发送Form方式
* response返回值为json
*/
object Login{
//feed有4种迭代取值方式:
// 1、queue(顺序取值,直到取完,所以要注意并发虚拟用户数与参数值一致,否则报异常),缺点:不适合持续迭代,只适合一次性并发场景
// 2、random(随机取值,会重复取值),缺点:取值存在重复,不适合唯一性取值的场景
// 3、shuffle 网上资料有这个参数,但是在脚本编写时,无法引用,表示不知如何引用到
// 4、circular(参数一旦用完,从头开始)
val feeder = csv("D:\\gatling\\src\\test\\resources\\data\\Test_data.csv").circular
val Content_type = Map("Content-Type" -> "application/x-www-form-urlencoded") //定义header
val login = feed(feeder)
.exec(http("Login_request")
.post( public_url + "/user/login")
.headers(Content_type)
.formParam("username","${login_name}")
.formParam("pwd","${password}")
.check(status.is(200))
.check(jsonPath("$..token").exists) //判断json中是否存在key
.check(jsonPath("$..token").saveAs("Get_token")) // 方法1:获取token值,并将值存入session中Get_token参数
//.check(regex("\"token\":\"(.*)\"").saveAs("Get_token")) // 方法2:获取token值,并将值存入session中Get_token参数
.check(jsonPath("$..*").saveAs("Get_body")) //方法1:获取response中body所有值,并存入session中Get_body参数
// .check(bodyString.saveAs("Get_body")) //方法2:获取response中body所有值,此方法获取的结果与jsonPath("$..*")类似
.check(header("Date").saveAs("Get_header")) //获取response的header中某个参数值
)
.pause(3) //设置思考时间3s
//修改session中信息
.exec(session =>{
val token_value = session("Get_token").as[String] //获取session中Get_token参数值
session.set("Get_tokens",token_value) //对session中添加一个新参数,并给新参数赋值
}
)
//打印session信息
.exec{ session => println(session)
session
}
}
/** 发送登录请求
* POST请求
* 发送JSON方式
* response返回值为json
*/
object Login_json{
val feeder = csv("D:\\gatling\\src\\test\\resources\\data\\Test_data.csv").circular
//val Content_type = Map("Content-Type" -> "application/json")
val Content_type = Map("Content-Type" -> "application/x-www-form-urlencoded")
val login = feed(feeder)
.exec(http("Login_request")
.post( public_url + "/user/login")
.headers(Content_type)
.body(StringBody("{\"username\":\"${login_name}\",\"pwd\",\"${password}\"}")).asJSON
.check(status.is(200))
.check(jsonPath("$..token").exists) //判断json中是否存在key
.check(jsonPath("$..token").saveAs("Get_token")) // 方法1:获取token值,并将值存入session中Get_token参数
)
.pause(3) //设置思考时间3s
//修改session中信息
.exec(session =>{
val token_value = session("Get_token").as[String] //获取session中Get_token参数值
session.set("Get_tokens",token_value) //对session中添加一个新参数,并给新参数赋值
}
)
//打印session信息
.exec{ session => println(session)
session
}
}
/**发送退出系统请求
* POST请求
*/
object Exit_system{
val loginout = exec(http("loginout")
.post(public_url + "/user/loginout")
.formParam("token","${Get_token}")
)
}
/*******设置场景******/
//设置场景1
val scn = scenario("wo玩的就是任性...") //设置场景名称,可随意定义
.exec(Home.home,Login.login,Exit_system.loginout) //调用用例
//设置场景2
val scn1 = scenario( "wo扯的都是淡...")
.exec(Login.login)
/*******执行场景策略******/
/* setUp(
//执行多个场景
//scn.inject(atOnceUsers(1)),
//scn1.inject(rampUsers(2) over(1))
)*/
setUp(
//执行单场景
scn1.inject(constantUsersPerSec(2) during(10)) //每秒运行2个虚拟用户,持续运行10s
)
}
4、pom.xml:
注:pom.xml中需修改<simulationsFolder>src/test/scala/basic</simulationsFolder> 中场景存放的路径,以及<simulationClass>testcase01</simulationClass> 中执行场景class类,否则采用maven执行指定场景
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>gatling</groupId>
<artifactId>gatling_test</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<scala.version>2.11.7</scala.version>
<encoding>UTF-8</encoding>
<gatling.version>2.1.7</gatling.version>
<scala-maven-plugin.version>3.2.2</scala-maven-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.gatling</groupId>
<artifactId>gatling-app</artifactId>
<version>${gatling.version}</version>
</dependency>
<dependency>
<groupId>io.gatling</groupId>
<artifactId>gatling-recorder</artifactId>
<version>${gatling.version}</version>
</dependency>
<dependency>
<groupId>io.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
<version>${gatling.version}</version>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
</dependency>
<dependency>
<groupId>io.gatling</groupId>
<artifactId>gatling-app</artifactId>
</dependency>
<dependency>
<groupId>io.gatling</groupId>
<artifactId>gatling-recorder</artifactId>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!-- Run all [matching] tests] on mvn test -->
<groupId>io.gatling</groupId>
<artifactId>gatling-maven-plugin</artifactId>
<version>${gatling.version}</version>
<configuration>
<simulationsFolder>src/test/scala/basic</simulationsFolder>
<!--<includes>-->
<!--<include>com.jane.DemoOne</include>-->
<!--</includes>-->
<!--<excludes>-->
<!--<exclude>Demo.scala</exclude>-->
<!--</excludes>-->
<simulationClass>testcase01</simulationClass>
</configuration>
<executions>
<execution>
<phase>test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<jvmArgs>
<jvmArg>-Dgatling.http.ahc.connectTimeout=6000000</jvmArg>
<jvmArg>-Dgatling.http.ahc.requestTimeout=6000000</jvmArg>
<jvmArg>-Dgatling.http.ahc.sslSessionTimeout=6000000</jvmArg>
<jvmArg>-Dgatling.http.ahc.pooledConnectionIdleTimeout=6000000</jvmArg>
<jvmArg>-Dgatling.http.ahc.readTimeout=6000000</jvmArg>
</jvmArgs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
5、执行:
(1),直接执行maven 执行(注:需要配置运行环境,路径:Run-->Edit Configurations..-->点击+,选择maven,在command line中输入:clean package -P test)
(2)、调试脚本执行:可以直接,右键选择“Engine”,再选择"Run Engine",在Engine执行框中,按回车键,再选择第几个执行场景,如果只有一个场景,按回车键即可
6、结果:由于当前测试应用系统要求性能较低,未发现啥性能问题,故不贴出分析过程和图标了,请各位见谅!
总结:
1、scala脚本开发,有一定难度,需要一定开发功底。
2、Gatling还缺少一些必要报表,比如TPS、吞吐量。
3、后续可集成到jenkins上,做持续集成。
4、Gatling相对于jmeter的优越性,暂时未能体会到,故后续慢慢摸索和探究,也希望其他大咖来讨论。
5、Gatling的国内网站较少资料,需要多看官网和国外论坛之类资料:比如
官网(重要):https://gatling.io/docs/current/http/
https://groups.google.com/forum/#!msg/gatling/FjjUi4hHoFs/C0nhb1yCz7MJ
https://blog.csdn.net/lb245557472/article/details/80967889
http://www.360doc.com/content/17/0222/11/28952034_631079492.shtml
https://my.oschina.net/u/3613710/blog/1554937
http://www.mamicode.com/info-detail-2159295.html
https://blog.csdn.net/qq_37023538/article/details/54950827
http://www.bigerhead.com/2016/11/330.html