让JMeter测试数据生成更容易
背景:
在软件测试过程中,为了确保系统的稳定性和可靠性,需要对各种场景进行全面的测试。而不同的场景往往需要各种各样的测试数据,这些数据需要具有一定的真实性和多样性,以模拟实际使用中的各种情况。然而,手动创建大量的测试数据是一项繁琐且耗时的工作,并且可能无法保证数据的质量和一致性。针对此情况,要么需要测试人员花费大量时间和精力手动编写数据,要么使用一些工具来自动生成测试数据。
1. Faker介绍
Faker 是一个 Python 库,主要用于生成各种类型的模拟数据,以在开发和测试过程中使用。Faker 就是这样一个非常实用的工具,它主要用来生成开发测试过程中的模拟真实数据,能够极大地提高测试效率和质量
它可以生成诸如人名、地址、电话号码、电子邮件、日期、时间、公司名称、职位、信用卡号码等各种看似真实的信息。这些模拟数据在测试软件功能、验证数据处理逻辑、填充数据库进行性能测试等方面非常有用。
通过使用 Faker,开发人员和测试人员可以快速创建大量具有不同特征和多样性的数据,而无需手动输入或从真实数据源获取。这有助于更全面地测试应用程序在不同数据条件下的行为,发现潜在的问题,并提高软件的质量和稳定性。
总之,Faker 是一个强大的工具,能够为开发和测试工作提供方便、高效的模拟数据生成解决方案
在JMeter中也可以使用faker来实现,这里使用的是javafaker的jar包
1.1 下载
-
访问java的maven仓库
-
输入javafaker进行查询
-
点击进入进行下载
-
下载成功
-
由于javafaker有依赖,还需要安装snakeyaml,使用如上方法进行下载
-
如果没有看到jar下载,点击view all
-
选择jar包下载即可
1.2 安装
前提:已下载2个jar包
把上面2个jar包放到JMeter的lib目录下
2. JMeter+Faker使用
案例:
同步用户数据接口,名称、姓别、年龄、手机号码、电子邮件、地址等数据批量自动生成
脚本编写:
步骤:
- 创建线程组
- 创建HTTP信息头管理器
- 创建BeanShell 预处理程序
- 创建调试取样器
- 创建查看结果树
- 创建HTTP请求
- 创建线程组,点击测试计划,右键选择线程-线程组直接创建即可
- 创建配置元件 - HTTP信息头管理器,发送post请求需要,否则请求不成功
-
创建前置处理器 - BeanShell 预处理程序
在此编写faker相应的代码,faker具体使用可参考apidoc,访问地址如下:
BeanShell中内容如下
import com.github.javafaker.Faker; Faker faker = new Faker(new Locale("zh-CN")); String name = faker.name().name(); String address = faker.address().fullAddress(); vars.put("name", name); vars.put("address", address); String cityName = faker.address().cityName(); vars.put("cityname",cityName);
-
创建调试取样器
-
创建察看结果树,运行查看
-
创建HTTP请求,可以把写入的变量进行调用,这里面使用的是postman创建的mock服务来返回响应值
路径:直接添写postman的mock server地址
参数:根据所需要的内容进行发送
3. JMeter的内置函数-自定义
JMeter本身的函数助手中已经提供了一些快捷的方法,但是依据不同项目的需求,如何能自定义方法呢?接下来我们的目标就是一起来学习研究如何把自定义的函数放到函数助手中。
目录:创建一个生成名称的方法,根据输入的参数中文或英文生成对应的名称
用法: ${__RandName}
扩展 JMeter 的函数可以分成下面几个步骤:
- 在 IDE 中新建 JAVA或Maven 项目,引入扩展 JMeter 函数所需的依赖
- 编写并实现自定义函数的代码,编译打包
- 将编译好的包拷贝至 JMeter 的lib目录
- 编辑JMeter测试脚本,使用自定义的函数
- 运行脚本,查看运行结果是否正确
3.1 新建JAVA项目
IDE使用IDEA进行项目的创建,可以使用普通的JAVA项目或者是MAVEN项目
普通的java项目创建,把JMETER_HOME/lib下的 ApacheJMeter_core.jar 和 ApacheJMeter_java.jar 复制到java项目下,并添加到Lib中
MAVAN项目创建
pom.xml,在 中加入 JMeter 的 ApacheJMeter_core 、ApacheJMeter_java和 ApacheJMeter_functions 依赖
<dependencies>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_core</artifactId>
<version>${jmeter-version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_functions</artifactId>
<version>${jmeter-version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
<version>${jmeter-version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
由于 ApacheJMeter_core 和 ApacheJMeter_functions 已经包含在 JMeter 的运行时中,所以后面编译打包出来的 jar 不需要包含它们,此处将这两个依赖的 scope 设为 provided。
保存 pom.xml 后,如果 Maven 没有开始自动下载相关的依赖文件,在项目上右键点击,选择 Maven > Update Project,完成依赖的下载。
3.2 实现代码
扩展 JMeter 函数,有两处要点:
-
实现函数功能的类所在的 package 的声明必须包含".functions"
JMeter 可以通过非 UI 方式运行,因为它的设计中让一些核心的类(比如 ApacheJMeter_core 等)可以在非 UI 运行方式下被优先加载进来,加载这些类的时候是通过命名规则来实现的。
所有实现 JMeter 函数的类必须包含".functions.“,因此我们自定义实现的类里也必须遵守这一规则,比如,类所在的 package 名称为"com.customized.functions”
-
实现类继承 org.apache.jmeter.functions.AbstractFunction
3.2.1 创建Package
1. 右键java,点击创建Package,输入内容com.demo.functions
2. 创建成功
3.2.2 继承实现AbstractFunction类
- 创建实现类
- 直接使用idea的提示
- 点击Implement methods
-
点击OK即可,默认生成相应的代码
package com.demo.functions; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.functions.AbstractFunction; import org.apache.jmeter.functions.InvalidVariableException; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; import java.util.Collection; import java.util.List; public class CardIdFunc extends AbstractFunction { @Override public String execute(SampleResult sampleResult, Sampler sampler) throws InvalidVariableException { return null; } @Override public void setParameters(Collection<CompoundVariable> collection) throws InvalidVariableException { } @Override public String getReferenceKey() { return null; } @Override public List<String> getArgumentDesc() { return null; } }
3.3 AbstractFunction功能介绍
ApacheJMeter_core 中的 AbstractFunction 类提供了4个抽象方法,在扩展的时候需要实现它们。
- execute:JMeter 会将上次运行的 SampleResult 和当前的 Sampler 作为参数传入 execute 方法中,方法的返回值就是在运行该函数后应得到的值,返回类型为 String 类型
public String execute(SampleResult previousResult, Sampler currentSampler) throws InvalidVariableException;
- getArgumentDesc:关于你实现的函数所需的参数的描述。
public List<String> getArgumentDesc();
- setParameters :传递用户在执行过程中传入的函数所需的实际参数值
public void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException;
- getReferenceKey:自定义的函数的名字,JMeter 约定的命名规则是在函数名前面加入双下划线"__"。建议函数的名字跟实现类的类名保持一致。
public String getReferenceKey();
3.4 示例代码
package com.demo.functions;
import com.demo.utils.FakerUtil;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.functions.AbstractFunction;
import org.apache.jmeter.functions.InvalidVariableException;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
public class NameRandomFunction extends AbstractFunction {
//自定义function的描述
private static final List<String> desc = new LinkedList<String>();
static {
desc.add("Get a random name within specified parameter Country.");
}
//function名称
private static final String KEY = "__FakerNameRandom";
private static final int MAX_PARA_COUNT = 1;
private static final int MIN_PARA_COUNT = 1;
//传入参数的值
private Object[] values;
@Override
public List<String> getArgumentDesc() {
return desc;
}
@Override
public String execute(SampleResult previousResult, Sampler currentSampler) throws InvalidVariableException {
try {
//获取参数1
String param1 = ((CompoundVariable) values[0]).execute().trim();
FakerUtil fakerUtil=new FakerUtil();
String name = fakerUtil.name(param1);
return String.valueOf(name);
} catch(Exception ex) {
throw new InvalidVariableException(ex);
}
}
@Override
public String getReferenceKey() {
return KEY;
}
@Override
public void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException {
checkParameterCount(parameters, MIN_PARA_COUNT, MAX_PARA_COUNT); //检查参数的个数是否正确
values = parameters.toArray(); //将值存入类变量中
}
}
代码解释:
-
函数名称定义,对应函数助手中的函数名称。例如:
__BeanShell
// function名称 private static final String KEY = "__FakerNameRandom"; //返回自定义的函数的名字 @Override public String getReferenceKey() { return KEY; }
-
变量存储以及验证
// 参数的数量 private static final int MAX_PARA_COUNT = 1; private static final int MIN_PARA_COUNT = 1; // 参数值验证 @Override public void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException { checkParameterCount(parameters, MIN_PARA_COUNT, MAX_PARA_COUNT); //检查参数的个数是否正确 values = parameters.toArray(); //将值存入类变量中 }
-
function描述
//自定义function的描述 private static final List<String> desc = new LinkedList<String>(); static { desc.add("Get a random name within specified parameter Country."); } @Override public List<String> getArgumentDesc() { return desc; }
-
运行该函数,这里面使用了javafaker类及方法
------------------------------ //此部分是AbstractFunction的内容 @Override public String execute(SampleResult previousResult, Sampler currentSampler) throws InvalidVariableException { try { //获取参数1 String param1 = ((CompoundVariable) values[0]).execute().trim(); FakerUtil fakerUtil=new FakerUtil(); String name = fakerUtil.name(param1); return String.valueOf(name); } catch(Exception ex) { throw new InvalidVariableException(ex); } } ------------------------------ package com.demo.utils; import com.github.javafaker.Faker; import java.util.Locale; import java.util.Objects; public class FakerUtil { public String name(String Country){ // 创建一个Faker实例 Faker faker=null; if(Objects.equals(Country, "china")){ faker = new Faker(Locale.CHINA); }else{ faker = new Faker(Locale.ENGLISH); } return faker.name().fullName(); } public static void main(String[] args) { FakerUtil fakerUtil=new FakerUtil(); fakerUtil.name("china1"); } }
3.5 项目打包
- pom.xml配置, 为打包内容
<?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>com.demo</groupId>
<artifactId>JMeter_Plugin_Test</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<jmeter-version>5.6.2</jmeter-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_core</artifactId>
<version>${jmeter-version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_functions</artifactId>
<version>${jmeter-version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_java</artifactId>
<version>${jmeter-version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
<build>
<finalName>jmeter-plugins-NameRandom-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<!-- 打包方式 -->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>assemble-all</id>
<phase>package</phase><!-- 绑定到package阶段上 -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
-
maven install,直接双击即可运行打包
-
打包成功后,在target目录生成,复制jar到JMETER_HOME\lib\ext下
3.6 自定义函数使用
-
复制到lib下后,需要重新启动Meter,才能生效。
-
打开函数助手,根据定义的函数名称进行查找
-
输入参数值 :china,生成中文名称
-
输入非china名称,则是英文,根据代码中编写的逻辑生成
3.7 执行结果验证
-
HTTP请求中使用函数,
"${__FakerNameRandom(c)}"
,参数非china为英文名称,china为中文名称 -
执行结果
-
输入china,
"${__FakerNameRandom(china)}"
-
执行结果
参考:
1. Faker API使用
以Address为例,点击Address类,查看其方法
具体使用
Faker faker = new Faker(new Locale("zh-CN"));
String cityName = faker.address().cityName();
2. Postman Mock Server使用
2.1 什么是Mock?
Mock:以可控的方式模拟真实对象行为的假的对象 ,可以根据自己的实际需求 返回想要的数据。
2.2 创建Mock Servers
1. 步骤:Mock Servers --> + (Create Mock Server)
-
添写Request Method、Requset URL等信息后,点击Next
- Request Method: get或post,为http请求时的方法
- Requset URL: 添加预期的方法
- Response Body: 添加接口的返回内容
-
这个页面可以设置Mock Server的名称等信息,点击Create Mock Server
- 创建成功后,会在Mock Server列表中显示,后面如何要使用,点击"Copy URl"即可复制Mock Server地址,例如:
https://93a51b99-a2e2-4084-b16a-cefd24e3c1a6.mock.pstmn.io
3. JMeter插件API
-
参考:官网API地址 https://jmeter.apache.org/api/org/apache/jmeter/functions/EscapeXml.html
-
案例参考: https://github.com/undera/jmeter-plugins/blob/master/plugins/functions/src/main/java/kg/apc/jmeter/functions/StrLen.java,具体代码如下:
package kg.apc.jmeter.functions; import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.functions.AbstractFunction; import org.apache.jmeter.functions.InvalidVariableException; import org.apache.jmeter.samplers.SampleResult; import org.apache.jmeter.samplers.Sampler; import org.apache.jmeter.threads.JMeterVariables; import java.util.Collection; import java.util.LinkedList; import java.util.List; /** * Provides a DoubleSum function that adds two or more Double values. * Mostly copied from LongSum */ public class StrLen extends AbstractFunction { private static final List<String> desc = new LinkedList<String>(); private static final String KEY = "__strLen"; static { desc.add("String to measure length"); desc.add("Name of variable in which to store the result (optional)"); } private Object[] values; /** * No-arg constructor. */ public StrLen() { } /** * {@inheritDoc} */ @Override public synchronized String execute(SampleResult previousResult, Sampler currentSampler) throws InvalidVariableException { JMeterVariables vars = getVariables(); Integer len = ((CompoundVariable) values[0]).execute().length(); if (vars != null && values.length > 1) { String varName = ((CompoundVariable) values[1]).execute().trim(); vars.put(varName, len.toString()); } return len.toString(); } /** * {@inheritDoc} */ @Override public synchronized void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException { checkMinParameterCount(parameters, 1); values = parameters.toArray(); } /** * {@inheritDoc} */ @Override public String getReferenceKey() { return KEY; } /** * {@inheritDoc} */ public List<String> getArgumentDesc() { return desc; } }
);
}
return len.toString();
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void setParameters(Collection<CompoundVariable> parameters) throws InvalidVariableException {
checkMinParameterCount(parameters, 1);
values = parameters.toArray();
}
/**
* {@inheritDoc}
*/
@Override
public String getReferenceKey() {
return KEY;
}
/**
* {@inheritDoc}
*/
public List<String> getArgumentDesc() {
return desc;
}
}