3. 测试脚本设计:业务场景建模、参数化与数据驱动、关联与断言、事务控制与思考时间
脚本设计这事儿,说白了就是把你对业务的理解,翻译成机器能跑的语言。我见过太多人一上来就录脚本,录完就跑,结果压测数据跟实际业务差了十万八千里。嗯,这一章咱们就聊聊怎么把脚本写“对”。
3.1 业务场景建模:别让脚本脱离真实
场景建模是脚本设计的起点。你想想看,用户真的会像你录脚本那样,机械地点击吗?不会。用户的行为是有路径、有分支、有思考的。
我个人习惯把业务场景拆成三层:
- 主路径场景:用户最常走的流程,比如“登录→搜索→加购→下单→支付”。这是压测的基线。
- 分支路径场景:用户可能走的其他路,比如“登录→搜索→查看详情→退出”。这些场景往往被忽略,但流量不小。
- 异常路径场景:用户输入错误、网络超时、服务降级时的表现。我曾在项目中遇到过,压测时只测主路径,上线后用户一输错密码,系统直接崩溃——因为没给异常留资源。
建模时,我建议用业务流程图先画一遍,再转成脚本逻辑。别跳步骤,画图能帮你发现很多隐藏的依赖关系。
3.2 参数化与数据驱动:别让所有用户用同一个账号
这是新手最容易犯的错。录完脚本,跑压测,1000个并发用户全用同一个账号登录。结果呢?服务端以为是攻击,直接封IP。或者数据库里一条记录被1000个线程同时更新,死锁了。
参数化,就是让每个虚拟用户用不同的数据。我常用的方式有几种:
| 参数化方式 | 适用场景 | 注意事项 |
|---|---|---|
| CSV 数据文件 | 用户账号、商品ID等固定数据集 | 注意文件编码和行数,别跑着跑着没数据了 |
| 函数生成 | 时间戳、随机字符串、UUID | 确保唯一性,别生成重复数据 |
| 数据库查询 | 从生产环境脱敏数据中提取 | 注意查询性能,别把压测变成数据库压测 |
| 计数器 | 需要递增序列的场景 | 多线程下注意线程安全 |
数据驱动更进一步——把测试数据和脚本逻辑彻底分离。脚本里只写流程,数据从外部文件读。这样换一套数据就能测不同的业务场景。我曾经用这个方式,一套脚本跑了20多个不同的业务分支,省了不少事。
3.3 关联与断言:让脚本学会“认人”
关联,说白了就是从一个请求的响应里,把动态值取出来,传给下一个请求。比如登录后返回的session ID、下单后生成的订单号。
我见过最惨的案例:脚本里写死了session ID,压测时所有用户共用一个会话。结果服务端以为是同一个用户在疯狂操作,直接限流。压测报告出来,TPS只有200,实际上服务能扛2000。
关联的常见手段:
- 正则表达式提取:最通用,但写起来麻烦。注意贪婪匹配和换行符。
- JSON/XML 路径提取:响应是结构化数据时首选,清晰且稳定。
- 边界值提取:用左右边界字符串定位,适合非结构化响应。
断言呢?就是检查响应对不对。别只检查HTTP状态码200就完事。200不代表业务成功——可能返回的是“系统繁忙,请稍后重试”。
// 一个典型的断言设计(伪代码)
// 1. 检查HTTP状态码 == 200
// 2. 检查响应体包含 "success": true
// 3. 检查响应时间 < 3000ms
// 4. 检查返回的订单号不为空
// 5. 检查数据库状态(可选,用于关键业务)
3.4 事务控制与思考时间:模拟真实用户节奏
事务,就是一组有业务意义的请求集合。比如“下单”这个事务,可能包含“提交订单→支付→查询订单状态”三个请求。事务的响应时间,是这三个请求的总耗时。
我个人习惯把事务定义得粒度适中。太细了,比如把“点击按钮”也算一个事务,没意义。太粗了,比如把“从登录到退出”算一个事务,出了问题你都不知道是哪一步慢的。
| 事务粒度 | 示例 | 分析价值 |
|---|---|---|
| 原子事务 | 单个API请求 | 定位具体接口性能问题 |
| 业务事务 | 下单流程(3-5个请求) | 评估端到端用户体验 |
| 用户会话 | 从登录到退出(10+请求) | 评估整体用户生命周期 |
思考时间,就是用户操作之间的停顿。用户不会像机器一样连续点击——他会看商品详情、犹豫一下、输入地址。思考时间设得太短,压测结果过于乐观;设得太长,又测不出系统真实瓶颈。
我常用的思考时间模型:
- 固定思考时间:简单,但不真实。适合基准测试。
- 随机思考时间:在[min, max]范围内随机取值。更接近真实。
- 基于日志的思考时间:从生产日志中提取用户操作间隔的分布,然后按分布模拟。这是最真实的,但实现成本高。
嗯,脚本设计这块,说到底就是“模拟真实”四个字。参数化让数据真实,关联让流程真实,思考时间让节奏真实。把这些做好了,压测结果才有参考价值。