头像
淇迹时刻
-- : -- : --
切换主题色
-- : -- : --

JMeter参数化测试详解

最后更新于:

JMeter参数化测试详解

一、参数化测试概述

1.1 什么是参数化测试

参数化测试是指在测试过程中使用不同的参数值来执行相同的测试脚本,以验证系统在不同输入下的行为。

1.2 参数化测试的作用

  • 增加测试覆盖:使用不同的参数值测试系统
  • 模拟真实场景:模拟不同用户的行为
  • 数据驱动测试:将测试数据与测试逻辑分离
  • 提高测试效率:一次执行多个测试用例

1.3 参数化测试方法

方法说明适用场景
CSV数据文件从CSV文件读取测试数据大量测试数据
用户定义变量在测试计划中定义变量少量固定数据
函数生成使用JMeter函数生成数据动态数据生成
数据库查询从数据库读取测试数据从数据库获取数据
JSR223脚本使用脚本生成数据复杂数据生成

二、CSV数据文件参数化

2.1 创建CSV数据文件

1username,password,email
2admin,admin123,admin@example.com
3user1,user123,user1@example.com
4user2,user234,user2@example.com
5user3,user345,user3@example.com
6user4,user456,user4@example.com
7user5,user567,user5@example.com

2.2 添加CSV数据设置

1右键点击线程组 → Add → Config Element → CSV Data Set Config

2.3 配置

1名称: CSV数据设置
2文件名: users.csv
3文件编码: UTF-8
4变量名称: username,password,email
5分隔符: ,
6允许带引号: true
7遇到文件结束符再次循环: true
8遇到文件结束符停止线程: false
9线程共享模式: All threads

2.4 参数说明

文件名(Filename)

1文件名: users.csv

说明:CSV文件的路径。可以使用绝对路径或相对路径。

示例

1绝对路径: C:\test\users.csv
2相对路径: users.csv(相对于JMeter工作目录)

文件编码(File Encoding)

1文件编码: UTF-8

说明:CSV文件的编码格式。

选项

  • UTF-8:UTF-8编码(默认)
  • GBK:中文编码
  • ISO-8859-1:ISO编码

变量名称(Variable Names)

1变量名称: username,password,email

说明:CSV文件中列的名称,用逗号分隔。

注意:变量名称必须与CSV文件的列顺序对应。

分隔符(Delimiter)

1分隔符: ,

说明:CSV文件的列分隔符。

选项

  • ,:逗号分隔(默认)
  • ;:分号分隔
  • \t:制表符分隔

允许带引号(Allow quoted data?)

1勾选: 允许带引号

说明:如果勾选,CSV文件中的值可以用引号包围。

示例

1username,password,email
2"test,user",123456,"test@example.com"

遇到文件结束符再次循环(Recycle on EOF?)

1勾选: 遇到文件结束符再次循环

说明:如果勾选,当读取到文件末尾时,重新从文件开头读取。

遇到文件结束符停止线程(Stop thread on EOF?)

1勾选: 遇到文件结束符停止线程

说明:如果勾选,当读取到文件末尾时,停止线程。

线程共享模式(Sharing Mode)

1线程共享模式: All threads

选项

  • All threads:所有线程共享同一个文件指针
  • Current thread group:当前线程组共享文件指针
  • Current thread:每个线程独立使用文件

2.5 使用场景

用户登录测试

 1测试计划结构示例:
 2测试计划
 3├── 线程组(10线程,循环5次)
 4│   ├── CSV数据设置
 5│   │   文件名: users.csv
 6│   │   变量名称: username,password,email
 7│   └── HTTP请求(登录)
 8│       方法: POST
 9│       路径: /api/login
10│       参数:
11│           username: ${username}
12│           password: ${password}

效果:每个线程使用不同的用户数据登录。

三、用户定义变量参数化

3.1 添加用户定义的变量

1右键点击线程组 → Add → Config Element → User Defined Variables

3.2 配置

 1名称: 测试数据变量
 2
 3变量列表:
 4    名称: username
 5    值: ${__RandomFromMultipleVars(user1,user2,user3,user4,user5)}
 6    
 7    名称: password
 8    值: 123456
 9    
10    名称: email
11    值: ${username}@example.com

3.3 使用场景

少量固定数据测试

 1测试计划结构示例:
 2测试计划
 3├── 线程组(10线程,循环10次)
 4│   ├── 用户定义的变量
 5│   │   ├── base_url = http://localhost:8080
 6│   │   ├── api_version = v1
 7│   │   └── timeout = 30000
 8│   └── HTTP请求
 9│       服务器名称: ${base_url}
10│       路径: /${api_version}/users

四、函数参数化

4.1 使用__Random函数

1${__Random(1,1000)}

示例

1测试计划结构示例:
2测试计划
3├── 线程组(100线程,循环10次)
4│   └── HTTP请求(查询用户)
5│       方法: GET
6│       路径: /api/users/${__Random(1,1000)}

4.2 使用__RandomString函数

1${__RandomString(10,abcdefghijklmnopqrstuvwxyz)}

示例

 1测试计划结构示例:
 2测试计划
 3├── 线程组(100线程,循环10次)
 4│   └── HTTP请求(创建用户)
 5│       方法: POST
 6│       路径: /api/users
 7│       Body Data:
 8│           {
 9│               "username": "${__RandomString(10,abcdefghijklmnopqrstuvwxyz)}",
10│               "email": "${__RandomString(8,abcdefghijklmnopqrstuvwxyz)}@example.com",
11│               "password": "${__RandomString(8,ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789)}"
12│           }

4.3 使用__UUID函数

1${__UUID()}

示例

 1测试计划结构示例:
 2测试计划
 3├── 线程组(100线程,循环10次)
 4│   └── HTTP请求(创建订单)
 5│       方法: POST
 6│       路径: /api/orders
 7│       Body Data:
 8│           {
 9│               "order_id": "${__UUID()}",
10│               "amount": ${__Random(100,10000)}
11│           }

4.4 使用__time函数

1${__time(yyyy-MM-dd HH:mm:ss)}

示例

1测试计划结构示例:
2测试计划
3├── 线程组(100线程,循环10次)
4│   └── HTTP请求(查询日志)
5│       方法: GET
6│       路径: /api/logs
7│       参数:
8│           start_time: ${__timeShift(yyyy-MM-dd HH:mm:ss,-1,h,)}
9│           end_time: ${__time(yyyy-MM-dd HH:mm:ss)}

五、数据库参数化

5.1 添加JDBC连接配置

1名称: JDBC连接配置
2变量名: mysql_db
3JDBC驱动类: com.mysql.cj.jdbc.Driver
4JDBC URL: jdbc:mysql://localhost:3306/test
5用户名: root
6密码: password

5.2 添加JDBC请求

1名称: 查询用户数据
2变量名: mysql_db
3查询类型: Select Statement
4查询语句: SELECT username, password FROM users WHERE status = 1 ORDER BY RAND() LIMIT 1;
5结果变量名: user_data

5.3 使用数据库数据

1// 在JSR223后置处理器中使用数据库查询结果
2def username = vars.getObject("user_data").get(0).get("username")
3def password = vars.getObject("user_data").get(0).get("password")
4vars.put("db_username", username)
5vars.put("db_password", password)

5.4 使用场景

 1测试计划结构示例:
 2测试计划
 3├── 线程组(100线程,循环10次)
 4│   ├── JDBC连接配置
 5│   ├── JDBC请求(查询用户)
 6│   │   查询语句: SELECT username, password FROM users WHERE status = 1 ORDER BY RAND() LIMIT 1;
 7│   │   结果变量名: user_data
 8│   ├── JSR223后置处理器(提取数据)
 9│   └── HTTP请求(登录)
10│       参数:
11│           username: ${db_username}
12│           password: ${db_password}

六、JSR223脚本参数化

6.1 添加JSR223前置处理器

 1名称: 生成测试数据
 2脚本语言: groovy
 3脚本:
 4    // 生成随机用户名
 5    def chars = 'abcdefghijklmnopqrstuvwxyz'
 6    def username = (1..10).collect { chars[new Random().nextInt(chars.size())] }.join()
 7    vars.put("username", username)
 8    
 9    // 生成随机邮箱
10    vars.put("email", "${username}@example.com")
11    
12    // 生成随机密码
13    def passwordChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
14    def password = (1..8).collect { passwordChars[new Random().nextInt(passwordChars.size())] }.join()
15    vars.put("password", password)
16    
17    // 生成随机年龄
18    vars.put("age", (18..60).getAt(new Random().nextInt(43)).toString())
19    
20    // 生成随机手机号
21    def phone = "1${[3,4,5,7,8].getAt(new Random().nextInt(5))}" + (1..9).collect { new Random().nextInt(10) }.join()
22    vars.put("phone", phone)

6.2 使用场景

复杂数据生成

 1测试计划结构示例:
 2测试计划
 3├── 线程组(100线程,循环10次)
 4│   ├── JSR223前置处理器(生成数据)
 5│   └── HTTP请求(创建用户)
 6│       方法: POST
 7│       路径: /api/users
 8│       Body Data:
 9│           {
10│               "username": "${username}",
11│               "email": "${email}",
12│               "password": "${password}",
13│               "age": ${age},
14│               "phone": "${phone}"
15│           }

七、参数化测试最佳实践

7.1 数据准备

11. 确保测试数据的真实性和多样性
22. 准备足够的测试数据
33. 数据格式要符合接口要求
44. 敏感数据要加密处理

7.2 参数化策略

11. 根据测试场景选择合适的参数化方法
22. 对于大量数据使用CSV文件
33. 对于动态数据使用函数或脚本
44. 对于数据库数据使用JDBC请求

7.3 数据管理

11. 测试数据与测试脚本分离
22. 使用版本控制管理测试数据
33. 定期更新测试数据
44. 清理测试产生的数据

7.4 性能考虑

11. CSV文件不要过大(建议小于1GB)
22. 使用线程共享模式减少文件读取次数
33. 避免在循环中频繁读取数据库
44. 使用缓存减少重复数据生成

八、实战案例

8.1 案例1:用户登录参数化测试

需求:使用CSV文件测试多个用户登录

配置步骤

  1. 创建CSV数据文件(users.csv):
 1username,password
 2admin,admin123
 3user1,user123
 4user2,user234
 5user3,user345
 6user4,user456
 7user5,user567
 8user6,user678
 9user7,user789
10user8,user890
11user9,user901
12user10,user1011
  1. 创建线程组(10线程,循环10次)

  2. 添加CSV数据设置:

1文件名: users.csv
2变量名称: username,password
3分隔符: ,
4遇到文件结束符再次循环: true
5线程共享模式: All threads
  1. 添加HTTP请求(登录):
1方法: POST
2路径: /api/login
3参数:
4    username: ${username}
5    password: ${password}
  1. 添加聚合报告

  2. 运行测试

8.2 案例2:动态数据参数化测试

需求:测试创建用户接口,使用动态生成的数据

配置步骤

  1. 创建线程组(50线程,循环5次)

  2. 添加JSR223前置处理器:

 1def chars = 'abcdefghijklmnopqrstuvwxyz'
 2def username = (1..10).collect { chars[new Random().nextInt(chars.size())] }.join()
 3vars.put("username", username)
 4
 5vars.put("email", "${username}@example.com")
 6
 7def passwordChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
 8def password = (1..8).collect { passwordChars[new Random().nextInt(passwordChars.size())] }.join()
 9vars.put("password", password)
10
11vars.put("age", (18..60).getAt(new Random().nextInt(43)).toString())
12
13def phone = "1${[3,4,5,7,8].getAt(new Random().nextInt(5))}" + (1..9).collect { new Random().nextInt(10) }.join()
14vars.put("phone", phone)
  1. 添加HTTP请求(创建用户):
 1方法: POST
 2路径: /api/users
 3Body Data:
 4    {
 5        "username": "${username}",
 6        "email": "${email}",
 7        "password": "${password}",
 8        "age": ${age},
 9        "phone": "${phone}"
10    }
  1. 添加聚合报告

  2. 运行测试

8.3 案例3:数据库数据参数化测试

需求:从数据库读取用户数据进行登录测试

配置步骤

  1. 下载MySQL驱动并复制到lib目录

  2. 创建线程组(100线程,循环10次)

  3. 添加JDBC连接配置:

1变量名: mysql_db
2JDBC驱动类: com.mysql.cj.jdbc.Driver
3JDBC URL: jdbc:mysql://localhost:3306/test
4用户名: root
5密码: password
  1. 添加JDBC请求(查询用户):
1变量名: mysql_db
2查询类型: Select Statement
3查询语句: SELECT username, password FROM users WHERE status = 1 ORDER BY RAND() LIMIT 1;
4结果变量名: user_data
  1. 添加JSR223后置处理器(提取数据):
1def result = vars.getObject("user_data")
2if (result != null && !result.isEmpty()) {
3    def row = result.get(0)
4    vars.put("db_username", row.get("username"))
5    vars.put("db_password", row.get("password"))
6} else {
7    vars.put("db_username", "admin")
8    vars.put("db_password", "admin123")
9}
  1. 添加HTTP请求(登录):
1方法: POST
2路径: /api/login
3参数:
4    username: ${db_username}
5    password: ${db_password}
  1. 添加聚合报告

  2. 运行测试

九、常见问题

9.1 CSV文件读取失败

回答

  • 检查文件路径是否正确
  • 检查文件编码是否正确
  • 检查分隔符是否正确
  • 确保文件存在且可读

9.2 变量值为空

回答

  • 检查变量名称是否正确
  • 检查CSV文件是否有数据
  • 检查文件编码是否正确
  • 使用调试后置处理器查看变量值

9.3 数据重复

回答

  • 增加CSV文件中的数据量
  • 使用随机函数生成数据
  • 使用数据库查询获取数据
  • 调整线程共享模式

9.4 性能问题

回答

  • 使用较小的CSV文件
  • 使用线程共享模式
  • 避免频繁读取数据库
  • 使用缓存机制

下一章12-断言测试.md

最新文章