一、测试背景与目的
在现代Web应用开发中,绝大多数需要用户认证的API接口都采用Session-Cookie或Token机制来维护用户会话状态。本次测试旨在验证某后端系统(192.168.1.6:3001)在完成用户登录认证后,各业务接口是否能正确识别并处理携带认证Cookie的请求。
本次测试目的: 管理员可为分销商设置模型定价倍率,分销商可为用户设置定价倍率,最终用户支付的费用 = 基础模型费 × 管理员倍率 × 分销商倍率,验证二级分销模式下,用户扣费、分销商扣费和分销商赚取三个金额的计算逻辑是否准确,确保资金流转正确无误。
二、JMeter录制流程
2.1 录制准备
- 启动本地项目:将待测试的后端服务部署并启动,确保服务正常运行在目标端口(如
3001) - 创建JMeter测试计划:
- 打开JMeter,新建测试计划
- 在测试计划下创建线程组(Thread Group)
- 创建HTTP(S) Test Script Recorder(录制控制器)
- 配置录制器:
- 将录制器的 Target Controller 设置为
Test Plan -> Thread Group - 配置目标服务器地址和端口
- 将录制器的 Target Controller 设置为
2.2 关键配置:绕过localhost限制
重要提示:JMeter等测试软件默认不会监听回环地址(127.0.0.1或localhost),这是为了防止监听到不必要的本机系统操作。
因此,在录制过程中,需要将浏览器或应用中的访问路径进行替换:
| 原始地址 | 替换为 |
|---|---|
localhost:3001 | 192.168.1.6:3001 |
127.0.0.1:3001 | 192.168.1.6:3001 |
2.3 开始录制
- 在录制器中点击 “开始” 按钮,启动代理服务
- 在浏览器或客户端应用中配置代理(地址为JMeter所在机器的IP,端口默认
8888) - 执行用户操作(登录、浏览页面等),JMeter将自动捕获所有HTTP请求
- 录制完成后,点击 “停止” 按钮
2.4 录制后处理
录制完成后,需要对录制的请求进行整理和优化:
- 删除静态资源请求(如图片、CSS、JS等无关请求)
- 调整请求顺序,确保登录接口优先执行
- 添加Cookie管理器以处理会话认证
三、测试环境
| 配置项 | 值 |
|---|---|
| 测试工具 | Apache JMeter 5.6.3 |
| 被测服务器 | 192.168.1.6:3001 |
| 协议 | HTTP |
| 测试用户 | root / zzz123… |
四、工作流程设计
测试采用顺序执行策略,确保每个请求按预期流程依次执行:
┌─────────────────────────────────────────────────────────────┐
│ Thread Group (1 Thread) │
├─────────────────────────────────────────────────────────────┤
│ 1. HTTP Cookie Manager │
│ └─ 自动管理Cookie,自动捕获登录响应中的Set-Cookie │
├─────────────────────────────────────────────────────────────┤
│ 2. POST /api/user/login │
│ └─ 用户登录,服务器返回Session Cookie │
├─────────────────────────────────────────────────────────────┤
│ 3. GET /api/status │
│ └─ 状态查询接口(携带登录Cookie) │
├─────────────────────────────────────────────────────────────┤
│ 4. GET /api/notice │
│ └─ 通知接口(携带登录Cookie) │
├─────────────────────────────────────────────────────────────┤
│ 5. GET /api/home_page_content │
│ └─ 首页内容接口(携带登录Cookie) │
└─────────────────────────────────────────────────────────────┘
五、关键技术实现
5.1 Cookie管理器配置
<CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager">
<collectionProp name="CookieManager.cookies"/>
<boolProp name="CookieManager.clearEachIteration">false</boolProp> <!-- 保持Cookie -->
<boolProp name="CookieManager.controlledByThreadGroup">false</boolProp>
</CookieManager>
关键参数说明:
clearEachIteration=false:确保Cookie在多次迭代间保持,避免重复登录controlledByThreadGroup=false:Cookie在线程组级别共享,同一线程的所有请求共享同一Cookie存储
5.2 登录请求配置
{
"username": "root",
"password": "zzz123..."
}
登录请求采用POST方法,Content-Type为application/json,请求头包含完整的浏览器模拟信息。
5.3 请求头管理
每个请求都配置了标准的HTTP请求头,模拟真实浏览器的请求行为:
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:152.0)Accept:application/json, text/plain, /Accept-Language:zh-CN,zh;q=0.9…Origin:跨域请求标识
六、测试执行步骤
- 打开JMeter,加载测试计划文件
Yun_he_Thread Group.jmx - 配置线程组参数:
- 线程数:1(单用户场景)
- ramp-up时间:1秒
- 循环次数:1
- 运行测试:点击绿色启动按钮执行测试
- 查看结果:
- 通过”View Results Tree”查看每个请求的详细响应
- 通过”Aggregate Report”查看性能统计指标
七、预期测试结果
| 请求 | 预期状态码 | 说明 |
|---|---|---|
| POST /api/user/login | 200 | 登录成功,获取Session |
| GET /api/status | 200 | 状态接口正常返回数据 |
| GET /api/notice | 200 | 通知接口正常返回数据 |
| GET /api/home_page_content | 200 | 首页内容正常返回 |
八、二级分销定价验证
基于测试目的,本次测试还需重点验证二级分销模式下的资金计算逻辑:
用户支付费用 = 基础模型费 × 管理员倍率 × 分销商倍率
测试场景:
| 角色 | 操作 | 验证点 |
|---|---|---|
| 管理员 | 设置分销商定价倍率 | 倍率是否正确生效 |
| 分销商 | 为用户设置定价倍率 | 倍率是否正确叠加 |
| 用户 | 调用扣费接口 | 最终扣费金额计算是否准确 |
九、常见问题排查
问题1:后续请求返回401未授权
- 原因:Cookie未被正确传递
- 解决:检查Cookie Manager配置,确保放在线程组首位
问题2:Cookie每次请求都变化
- 原因:
clearEachIteration=true - 解决:修改为
false,保持会话
问题3:录制不到请求
- 原因:使用了localhost或127.0.0.1访问
- 解决:改为使用本机IPv4地址访问
问题4:登录成功但后续接口仍失败
- 原因:服务器要求特定Cookie名称(如
session_id) - 解决:在Cookie Manager中配置Cookie的白名单/黑名单规则
十、扩展测试场景
10.1 多用户并发测试
<intProp name="ThreadGroup.num_threads">10</intProp> <!-- 10个并发用户 -->
10.2 循环压力测试
<stringProp name="LoopController.loops">100</stringProp> <!-- 每人循环100次 -->
10.3 关联Token提取
如服务器返回JWT Token,可使用Regular Expression Extractor提取后通过Header Manager传递给后续请求:
<RegularExpressionExtractor guiclass="RegexExtractorGui" ...>
<stringProp name="RegexExtractor.regex">token=([^;]+)</stringProp>
<stringProp name="Template">$1$</stringProp>
<stringProp name="DefaultValue">NOT_FOUND</stringProp>
</RegularExpressionExtractor>