文章

Jmeter 实用技巧

最近使用 jmeter 做了部分压测的工作,记录一下在压测过程中使用到的一些有用的方法和技巧。

随机

自带函数

函数助手

${__Random(,,)}

生成指定范围内的随机数 例如:${__Random(1,100,num) 会生成 1-100 之间的随机数,存到变量num

${__RandomDate(,,,,)}

生成指定范围的随机日期 例如:${__RandomDate(yyyy-MM-dd,2019-12-12,2020-12-12,,date)} 会生成 2019-12-12 ~ 2020-12-12 之间的随机日期,存到变量 date

${__RandomFromMultipleVars(,)}

从指定的变量中随机获取一个 例如:${__RandomFromMultipleVars(a|b|c,random_var)} 会从 a/b/c 三个变量中随机取一个,存到变量 random_var (第一个参数变量名之间用 | 分隔)

${__RandomString(10,1234567890qwertyu,random_str)}

从指定字符中生成指定长度的随机字符串 例如:${__RandomString(10,1234567890qwertyu,random_str)} 会从 1234567890qwertyu 随机取 10 个字符生成一个字符串存到变量 random_str

代码实现

Jmeter 提供了前置处理器/后置处理器,在处理器脚本里面可以通过代码生成随机数,不受限于自带函数,脚本语言支持 groovy/Java/JavaScript 等多种,官方推荐使用 groovy

保存 CSV 文件

使用 BeanShell 后置处理器,可以通过代码将数据写到 CSV 文件。例如:在执行登录接口之后 tokenphone 保存到 CSV 中,其他的测试计划可以引入 CSV 文件使用 tokenphone,具体代码:

1
2
3
4
5
6
7
f = new FileOutputStream("/Users/white/Desktop/user_token.csv", true);
p = new PrintStream(f); 
this.interpreter.setOut(p); 
var user_token = vars.get("user_token");
var phone = vars.get("phone");
print(user_token+","+phone);
f.close();

断言

Jmeter 提供了响应断言、JSON 断言、大小断言、XPATH 断言等等多种方便的断言可以直接使用,可以满足大部分的断言需求,但是如果需要对响应的内容做特殊判断的话就需要 JSR233 断言了。

JSR223 断言

函数助手

自己写代码断言,例如,一个 json 响应的结果是 {"success":"true", "data":null} 如果仅仅需要根据 success 判断是否通过断言,我们使用 JSON 断言即可,但是如果还要判断 data 不为空,那么 JSON 断言就无法满足需求了,除非写两个 JSON 断言。这种情况如果使用 JSR223 断言自己写代码的话,就可以完全根据自己的需求来判断,具体写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import groovy.json.*

def slurper = new JsonSlurper();
def output = new JsonOutput();
def json = slurper.parseText(prev.getResponseDataAsString());

if (!json.success) {
	AssertionResult.setFailureMessage(output.toJson(json.error));
	AssertionResult.setFailure(true);
} else {
  if(!json.data) {
    AssertionResult.setFailureMessage("empty data");
    AssertionResult.setFailure(true);
  }
}

使用 groovy 脚本解析响应的 JSON,对字段做判断再设置断言结果。手动设置 FailureMessage 也方便后面在测试报告中查看具体的异常。

复杂 JSON 中提取参数

场景:从前一个接口返回的列表中随机取满足某一条件的一项做为后一个接口的参数。

JSON 提取器和正则表达式提取器很难满足上述需求,使用 JSR223 后置处理器(JavaScript) 就可以通过代码实现,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var request_success = prev.isSuccessful();
if (request_success) {
    var json = JSON.parse(prev.getResponseDataAsString());
    if (success == true) {
        var data = json.result.data;
        if (data.length != 0) {
            log.info('the size of data is greater than zero');
            log.info("randomly select a item");
            var index = Math.floor(Math.random() * data.length);
            var item = data[index];
            vars.put("item", item);
        }
    }
}

While 控制器

只要满足条件,就会一直执行控制器内的请求,适用场景:轮询。 轮询的接口一般会有一个字段标识是否需要继续轮询,将该字段存到 vars 中,While 控制器判断该字段来决定是否继续请求。可以配合固定定时器每隔 1s 请求一次。

if 控制器

只要满足条件,就会执行一次控制器内的请求,适用场景:后一个接口依赖于前一个接口的返回,判断前一个接口的返回值是否满足。例如前面提到的从复杂 JSON 中提取参数的例子,如果没有找到满足条件的参数,可以将一个变量设置为 false,放到 vars 里面。然后在 if 控制器中判断这个变量。

循环控制器

循环执行控制器内的请求,可以指定循环次数或者无限循环,适用场景:前一个接口返回的数组内容作为参数依次调用后一个接口。同样以前面从复杂 JSON 中提取参数作为例子,现在不需要提取参数了,直接将 data 的大小作为循环次数,按下标依次取 data 的每一项作为参数执行第二个接口。

本文由作者按照 CC BY 4.0 进行授权