简介
Apache JMeter是Apache组织开发的基于Java的压力测试工具。开源,纯JAVA,可二次定制化开发。多线程框架,支持多并发操作。用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域。 它可以用于测试静态和动态资源,例如静态文件、Java 小服务程序、CGI 脚本、Java 对象、数据库、FTP 服务器, 等等。JMeter 可以用于对服务器、网络或对象模拟巨大的负载,来自不同压力类别下测试它们的强度和分析整体性能。另外,JMeter能够对应用程序做功能/回归测试,通过创建带有断言的脚本来验证你的程序返回了你期望的结果。为了最大限度的灵活性,JMeter允许使用正则表达式创建断言
JMETER基本概念
1、 测试计划:顶级菜单,代表一个测试计划
2、 线程组:代表一个要测试的场景(各种相关的交易集合),对于性能测试来说可以指定多少个用户完成这个场景的内容,对于自动化测试来说,指定一个用户即可
3、 控制器:挂在线程组下,用来控制各种交易(接口)的调用频率,有循环控制器、简单控制器等等
4、 取样器:代表一次发起请求和接收响应的元件,这里可以认为是一个接口、交易、请求
5、 前置处理器:取样器发起请求前可以用这做一些工作,比如参数化等等
6、 后置处理器:取样器收到响应后做一些工作,比如提取响应报文中的内容
Jmeter运行环境搭建
需要安装JDK
JDK -- JAVA开发工具包
JRE -- JAVA运行时环境
JVM -- JAVA虚拟机验证机器是否安装好Java环境
java -version 版本号 (在cmd里敲,看版本是否符合安装Jmeter需要的版本)
java 验证系统的环境变量path是否设置OK (出现很多选项,证明环境也没问题)设置环境变量--目标:任意路径可以识别jmeter
• 变量名:JMETER_HOME
D:\Tools\apache-jmeter-5.1.1 新建环境变量,找到apache-jmeter-5.1.1 文件夹,在地址栏处右键点击该文件夹,选择【将地址复制为文本】,粘到值的地方,确定
• 变量名:PATH
D:\Tools\apache-jmeter-5.1.1\bin
%JMETER_HOME%\bin --推荐使用这个 点击path→编辑→在最后一行写入内容
环境变量:
用户变量
比如多人共用一台电脑的时候,A登录的时候,系统调用的是A的用户变量。B登录的时候,系统调用的是B的用户变量。A和B之间互相看不到对方的内容。当然,如果一个人一台电脑,那么这个用户变量不需要设置。系统变量
是所有用这台电脑的用户都能看到并进行使用的变量
上面的环境变量配置也可以不配JMETER_HOME,只配置PATH,把全的路径写入里面即可↓
D:\Tools\apache-jmeter-5.1.1\bin
下载好Jmeter以后在bin文件夹里有几个需要关注的文件
- jmeter.bat Windows环境里运行的执行文件
- jmeter.log 日志文件
- jmeter.properties 属性文件
- jmeter.sh Linux环境里运行的执行文件(因为Java是跨平台的,因此不管是Windows,Mac还是Linux,Jmeter的安装包都是一个)
- jmeter-server 假如在Windows里如果启动单台Jmeter,直接点击jmeter.bat即可,但是分布式用jmeter.bat搞不定
线程组:
• setUp线程组:初始化,是第一个运行的,与位置无关。可以用于测试准备,比如用它来创建测试用户等
• 线程组:普通线程组
• tearDown线程组:在主线程结束后执行。可以用于测试清理工作,比如删除测试用户等
Jmeter 工具里的Generate HTML Report用法
- 新建一个csv文件,用来保存运行结果
- Jmeter的察看结果树→【所有数据写入一个文件】处浏览选择新建的csv文件
- 运行测试内容,运行完打开csv文件发现里面有内容,说明内容已成功保存到csv文件中
- Jmeter→工具→Generate HTML Report→如图将文件添加到相应位置
Results file(csv or jtl):自己建的csv文件
user.properties file: 安装的Jmeter的bin文件夹中的user.properties文件/jmeter.properties文件
Output directory: 将生成的html报告放在什么地方 - 点击下方的Generate Report,生成完后在指定的文件夹里会有一个以.html结尾的文件,打开就可以看见结果
接口
API(Application Programming Interface,应用程序接口)
是一些预先定义的函数,或指软件系统不同组成部分衔接的约定。目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问原码,或理解内部工作机制的细节
常见的Web接口方式
HTTP,HTTPS,Webservice
接口测试
是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。比如将前端界面请求通过HTTP协议请求到后端响应的过程
传统测试,你是不是得等前后端都完成你才能进行测试,才能进行自动化代码编写。 而如果是接口测试,只需要前后端定义好接口,那这时自动化就可以介入编写接口自动化测试代码,手工测试只需要后端代码完成就可以介入测试后端逻辑而不用等待前端工作完成
配置元件
1. HTTP请求默认值:
该组件可以为我们的http请求设置默认的值
创建一个测试计划有很多个请求且都是发送到相同的Server,只需添加一个Http request defaults组件并设置“Server Name or IP”。添加多个http请求会默认使用Http request defaults组件设置的值
线程组:为了模拟并发时的多个用户,线程数即为用户数
需求
消息体数据的内容在测试用例上会有标明(这里的截屏和需求有些许差异,实际按照测试用例的数据填即可)。消息体数据部分的格式可以用抓包工具抓包后复制过来(如上面的抓包工具的截屏图片里最后一行就是这个格式)
运行一下看“查看结果树”:
响应数据中:success:false
请求的Request Headers里Content-Type:text/plain,发现此处有误
在登录接口处↓
Content-Type在抓包工具的截屏的图片里有
请求头中的content-type,就是 B端发给S端的数据类型描述 。即告诉服务器端,我给你传的数据是某种类型的,服务器端针对不同类型的数据,做法当时是不相同的。响应头中的content-type,就是 S端发给B端的数据类型描述 。即告诉浏览器端,我给你发的是什么样格式的数据,那么浏览器端针对不同类型的数据,处理方法不同。
再运行,发现请求的Request Body里显示[no cookies]
复制登录接口,粘贴出来一份,改名为首页登录,里面的方法换成GET,路径换成/
同时为了让首页登录获得的cookie传给登录接口,还需要在线程组里添加↓,再运行就成功了
如果配置了HTTP请求默认值,那么接下来新建的所有的HTTP请求的协议,服务器名称或IP以及端口号就都按照这个默认值里写的来,每个里面都不用填这三项了。这样以后如果有变化,只需要改这个里面即可
例子1:获取TOKEN
1、Token的引入:Token是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生
2、Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码
3、使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮
需求
例子2:新增用户(要在获取token的成功以后才能做这个,因为这个是需要token的)
Jmeter里用${变量名}的方式提取变量
图中四个红框里的内容解说
- 引用名称
Jmeter变量的名称,存储提取的结果;即下个请求需要引用的值、字段、变量名,后文中引用方法是$ - 正则表达式
使用正则表达式解析响应结果,()括号表示提取字符串中的部分值,前后是提取的边界内容 - 模板
如果正则表达式有多个提取结果,则结果是数组形式,模板$1$,$2$等等,表示把解析到的第几个值赋给变量;从1开始匹配,以此类推。 用$$括起来, 这里用$1$ - 匹配数字
正则表达式匹配数据的结果可以看做一个数组,表示如何取值:0代表随机取值,正数n则表示取第n个值(比如1代表取第一个值),负数则表示提取所有符合条件的值。 - 缺省值
匹配失败时候的默认值;通常用于后续的逻辑判断,一般通常为特定含义的英文大写组合,比如:ERROR等
"token":"(.+?)" 的意思为:
从 "token":" 开始取,非贪婪模式,取到 "
原本get_token的结果为:{"token":"eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJKMjAxOTAzMDcwMDY0Iiwic3ViIjoiSjIwMTkwMzA3MDA2NCIsImlhdCI6MTYwMzAwOTI0Nn0.LHevQ0loA-5DrdIpUVIzP8LN-FmZnXwPUe1OSP8MeSY","code":"0000","msg":"成功","success":true}
用这个模式最后匹配的结果为:eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJKMjAxOTAzMDcwMDY0Iiwic3ViIjoiSjIwMTkwMzA3MDA2NCIsImlhdCI6MTYwMzAwOTI0Nn0.LHevQ0loA-5DrdIpUVIzP8LN-FmZnXwPUe1OSP8MeSY
正则表达式解说
. 表示匹配任意字符串
+表示匹配一次或多次
?表示匹配第一个匹配项后即停止匹配
*表示匹配0次或多次
正则表达式的匹配有两种模式:贪婪和非贪婪
贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配
而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配
假设有如下响应结果(只截取了其中一部分):
"code":"0","msg":"请求成功","bizSeqNo":"1804242UD01154300109392900987311" ,"result":{"bizSeqNo":"1804242UD01154300109423800987316","transactionTime":"20180424094239"
现在从中提取bizSeqNo的值:
"bizSeqNo":"(.*)":贪婪模式,提取结果是:1804242UD01154300109392900987311" ,"result":{"bizSeqNo":"1804242UD01154300109423800987316","transactionTime":"20180424094239
"bizSeqNo":"(.*?)":非贪婪模式,提取结果是:1804242UD01154300109392900987311
监听器元件
1. 察看结果树
●分析查看具体某个请求的详情(请求头,请求体,响应头,响应体)
●在做性能场景的时候,分析错误请求的原因
▼在高并发的时候,为了看哪条出错,需将[仅错误日志]勾选上,这样就只显示出错的部分
2. 聚合报告
汇总统计
▼请求数
▼响应时间(平均的,90%,95%,99%,max,min) 单位是毫秒
▼错误率--越低越好
▼吞吐量--越高越好
▼发送/接收--带宽
3. 用表格查看结果
4. 图形结果
其他元件
1. 前置处理器
请求发出去之前执行的元件,哪里需要放前置处理器就放到哪个里面。但是在里面的前后顺序不用管
例如:
加密 例如md5
参数化获取当前日期
获取随机字母数字名称
获取当前日期下月日期
2. 后置处理器
请求发出去之后执行的元件
例如:提取响应报文中的内容,获取Cookie、token
3. 定时器,以下为列举,还有其他很多种
▼思考时间--固定定时器
如果你需要让每个线程在请求之前按相同的指定时间停顿,那么可以使用这个定时器。模拟用户发出一个请求之后可能会有间隔进行下一次请求的场景
▼同步定时器--集合点
阻塞线程,直到指定的线程数量到达后,再一起释放,可以瞬间产生很大的压力,高并发,和网购时的秒杀活动感觉类似
▼高斯随机定时器
如需要每个线程在请求前按随机时间停顿,那么使用这个定时器,右图表示暂停时间会分布在100到400之间,计算公式参考:Math.abs((this.random.nextGaussian() * 300) + 100)
▼均匀随机定时器
和高斯随机定时器的作用差异不大,区别在于延时时间在指定范围内且每个时间的取值概率相同,每个时间间隔都有相同的概率发生,总的延迟时间就是随机值和偏移值之和
下面表示的是随机延迟时间的最大值是100毫秒:
(1)Random Delay Maximum(in milliseconds):随机延迟时间的最大毫秒数
(2)Constant Delay Offset(in milliseconds):暂停的毫秒数减去随机延迟的毫秒数
▼固定吞吐量定时器
可以让JMeter以指定数字的吞吐量(即指定TPS,只是这里要求指定每分钟的执行数,而不是每秒)执行
吞吐量计算的范围可以为指定为当前线程、当前线程组、所有线程组等范围,并且计算吞吐量的依据可以是最近一次线程的执行时延
4. 断言
响应断言就是判断响应信息里,是否有我们想要的信息。如果有,就说明测试通过,如果没有,就说明测试不通过,那就可能有bug了
例如上个截屏例子当中,登录接口下面添加→断言→响应断言
在测试模式当中填入:操作成功
意味着如果响应信息里有操作成功这个字样就说明测试通过,如果没有且将[仅错误日志]勾选上,那么就会显示出这个错误
断言是有作用域的,想要断言哪一个就将断言放在哪一个的里面。如果直接放在线程组的下面,就是对线程组里所有的东西进行断言
①响应断言
是最常用的一种断言方法,它可以对各种返回类型的结果进行断言,比如Test、html、application/json等
在需要进行断言的Sample,点击右键→添加→断言→响应断言
• 名称:Response Assertion名称
• Apply to
Main sample and sub-samples:作用于父节点取样器及其子节点取样器
Main sample only:仅作用于父节点取样器
Sub-samples only:仅作用于子节点取样器
Jmeter Variable Name to use:作用于Jmeter变量(输入框中可输入Jmeter的变量名称)
• 测试字段
响应文本:匹配从服务器返回的响应文本(不包括Response Headers)
响应代码:匹配响应状态码,比如200、304、404等
响应信息:匹配响应信息 ,如:OK,Not Modified、Not Found等
响应头:匹配响应头
请求头:匹配请求头
URL样本:匹配URL链接,对于HTTP请求,若勾选了“Follow Redirects”,则包含重定向后的URL
文档(文本):匹配文档内容
忽略状态:一个请求多项响应断言时,忽略某一项断言的响应结果,而继续下一项断言
请求数据:匹配请求数据,对于HTTP协议来说,是请求的message-body(不包括请求行与首部字段)
• 模式匹配规则
包括:如果文本包含正则表达式模式,则为true
匹配:如果整个文本与正则表达式模式匹配,则为true
相等:如果整个文本等于模式字符串(区分大小写),则为true。不支持正则表达式
字符串:如果文本包含模式字符串(区分大小写),则为true。不支持正则匹配
否:不进行匹配就算是Pass
或者:将多个测试模式以逻辑“或”组合起来
• 测试模式
需要匹配的正则表达式、字符串。可以添加多项,每一项会分开进行验证,若某一项验证失败,则其后的不会再进行验证
• 自定义失败消息
执行失败时显示的内容
添加查看结果树,运行
以下可观察到响应数据中是包含所指定的验证字符串,Pass
②Json断言
它只能针对响应结果是applicaton/json格式的请求进行断言
在需要进行断言的Sample,点击右键→添加→断言→JSON断言
使用json断言,一般是断言某个字段值是否返回的是我们需要的,所以需要写JSON PATH,下图使用正则模式匹配返回的结果包含40就认为响应结果满足要求,断言配置和响应结果如下所示
下面展示断言结果为常量时,如何进行断言设置
③BeanShell断言
支持各种开发语言,本文介绍使用java编写断言,使用BeanShell断言的好处是可以自由发挥,比如当断言失败,提示预期结果、实际结果,或者失败时把结果输出到日志
JMeter在它的BeanShell中内置了变量,用户可以通过这些变量与JMeter进行交互,其中主要的变量及其使用方法如下:
• log:写入信息到jmeber.log文件,使用方法:log.info(“This is log info!”);
• ctx:该变量引用了当前线程的上下文,使用方法可参考:org.apache.jmeter.threads.JMeterContext
• vars - (JMeterVariables):操作jmeter变量,这个变量实际引用了JMeter线程中的局部变量容器(本质上是Map),它是测试用例与BeanShell交互的桥梁,常用方法:
a) vars.get(String key):从jmeter中获得变量值
b) vars.put(String key,String value):数据存到jmeter变量中
更多方法可参考:org.apache.jmeter.threads.JMeterVariables
• props - (JMeterProperties - class java.util.Properties):操作jmeter属性,该变量引用了JMeter的配置信息,可以获取Jmeter的属性,它的使用方法与vars类似,但是只能put进去String类型的值,而不能是一个对象。对应于java.util.Properties。
a) props.get("START.HMS"); 注:START.HMS为属性名,在文件jmeter.properties中定义
b) props.put("PROP1","1234");
• prev - (SampleResult):获取前面的sample返回的信息,常用方法:
a) getResponseDataAsString():获取响应信息
b) getResponseCode() :获取响应code
更多方法可参考:org.apache.jmeter.samplers.SampleResult
• sampler - (Sampler):gives access to the current sampler
在这里除了可以使用beanshell的内置变量外,主要通过 Failure 和 FailureMessage来设置断言结果
在需要进行断言的Sample,点击右键→添加→断言→BeanShell断言
如果使用包含匹配,只需要修改预期结果即可,预期结果需要写在双引号之间,中间的双引号需要添加\转义,如下
使用完全相等匹配,并且失败时,输出结果到日志,本次把预期结果改错,所以会输出响应结果到日志
④Size Assertion(数据包字节大小断言)
判断响应结果是否包含正确数量的byte。可定义(=, !=, >, <, >=, <=)
⑤Duration Assertion(持续时间断言)
判断是否在给定的时间内返回响应结果
参数化技术
▼什么时候用参数化技术
1.账号需要多点登录的时候
如果账号是单点登录,那么A登录以后B用同样的账户登录时,A会被顶号。这时需要用多点登录解决
2.增加用户的时候,每个用户必须要用手机号注册,且每个用户的手机号需唯一
▼什么是参数化技术
是自动化测试脚本的一种常用技巧。是将脚本中的某些输入使用参数来代替,在脚本运行时指定参数的取值范围和规则
▼参数化流程
1.找出需要做参数化的数据
2.准备提供给参数化需要的数据源
3.把脚本里的常量→变量(使用前面的数据源数据)
▼参数化方式
1.CSV--需要配置的元件
使用场景:账号,密码
2.函数式
random
time
counter计数器
3.变量
4.编程式
引入外部的jar, java class
使用beanshell编程
1. CSV 参数化例子
以上消息体数据的userName和password的部分是需要参数化的
自己准备一个txt文件,里面写上每个学生的账户和密码
文件名:将账户和密码的txt文件的绝对路径写入。注意:最好是放在Jmeter的bin文件夹里,这样谁拷贝这个文件都能跑起来,不然没有存文件的人的相应盘符的话,就挂掉了
文件编码:UTF-8 (有中文字符的时候,都要写为这个文件编码)
变量名称:name,password 将txt文件里每列代表什么变量写上,中间用英文逗号相隔
忽略首行:txt文件里如果有title的部分,这个就需要为true,没有就false
分隔符:,
线程共享模式:不对外的时候选择当前线程组即可
将蓝框的部分修改成上一步中变量名称的名字,注意引用变量的格式为${变量名}
运行一下看结果为成功,在请求的Request Body里能看到
POST data:
{"userName":"J201903070058","password":"362387357"}
☆想跑多个的时候在线程组的<线程数>以及<Ramp-Up时间>处填上数量,再运行,发现会出现很多条结果,顺序就是按照txt文档里从上到下的顺序执行的
2. 函数式例子
添加用户的那个例子还可以用以下方法实现
JSON提取器是专门用来对返回的响应结果是application/json格式的报文进行提取
$.token :在根目录下调用token
JSON的基本语法就是$.然后跟要取的字段名,比如要取data,就直接写$.data,如果要取msg,就直接写$.msg
JSON Path expressions:变量路径
Match No.(0 for Random):当变量会获取到多个值时,取第几个,只有一个的时候可以置空
Default Values:当获取不到变量时,默认值为什么,一般置空即可
"msg":"TOKEN值为空"
解决方案:在请求头增加一个token
"message":"客户姓名必须为中文??"
解决方案:请求编码设置--uft-8
"message":"该客户手机号码已存在"
解决方案:必须做参数化--手机号码
选择Random
最小值和最大值分别输入之后点击【生成】
因为手机号码前3位就想让它为135,所以只要后8位产生随机数即可,因此填的数字为8位数
将拷贝并粘贴函数字符串的内容粘到【add_user】的【消息体数据】的的相应位置
运行之后结果OK
Json Path
内容取自以下链接
https://cloud.tencent.com/developer/article/1523400
JSON Path expressions写什么?
需要把查看结果树的显示格式改成JSON Path Tester,该查看结果树还支持直接对结果进行调试
JSON的基本语法就是$.然后跟要取的字段名,比如要取data,就直接写$.data,如果要取msg,就直接写$.msg
如果还要往下取值,就像文中的token,就需要继续在后面加".",(.代表子元素),然后跟字段名,如$.data.token
数组格式的怎么取值?
如下图所示,results保存了数组格式的值,先要知道数组索引,然后才能继续读取数组下的值,只要从0依次递增就能取到需要的值,如resulst[0],resulst[1],比如我想取第一个数组中的name值,只需要填写$.data.results[0].name
实际工作中,可能返回的查询结果经常会变化,这时候如果在写死数组索引的位置,比如上面的那样写成$.data.results[0].name,如果系统新增了项目,可能就会导致取的值不正确,这时候就需要用到另外一种取值方式,$..results[?(@.name=='function')].id,该json path可以通过一个变量定位到自己所在的数组的索引,然后在去取需要的id,比如automation这个值是唯一值,就可以通过该值取到需要的值,语法如下$..然后后面跟数组名称,本文为results,然后跟[?(@.name=='function')],其中name是序列中的一个唯一值,可以根据需要修改,function是name实际为什么值就填什么,最后在加"."后面跟你需要取的字段名如id,pah都行,只要在这个数组中存在
经常用到的json 转换网站, https://www.json.cn/ 打开该网站,拷贝json格式响应结果到该网站,系统就会帮你自动转换成json层级格式,方便调试
JSON PATH语法
$ :表示根元素
@:当前元素
. or []:子元素
n/a:父元素
..:递归下降,JSONPath是从E4X借鉴的
*:通配符,表示所有的元素
n/a:属性访问字符
[]:子元素操作符
[,]:连接操作符在XPath 结果合并其它结点集合。JSONP允许name或者数组索引
[start:end:step]:数组分割操作从ES4借鉴
?():应用过滤表示式
():脚本表达式,使用在脚本引擎下面
n/a:Xpath分组
JSON怎么通过一个提取器提取多个变量?
多个变量时则每个变量用“;”间隔开,正则表达式、匹配序列、默认值也一样,需要用“;”隔开,下面先介绍当正则表达式取固定的序列的时候,填写的规则如下
当表达式取任意序列时(匹配所有符合的值),填写规则如下图
3. 变量例子
因为HTTP请求默认值的服务器名称或IP处调用了变量IP,因此以后如果IP有改动的话只需要改用户自定义的变量处的IP地址即可