3. 脚本录制与回放:HTTP代理服务器录制、Badboy录制、手动编写脚本基础
脚本获取是性能测试的起点。本章聚焦三种主流脚本构建方式:HTTP代理服务器录制、Badboy工具录制以及纯手动编写。每种方式适用场景不同,掌握其原理与操作要点,能让你在面对各类被测系统时快速产出高质量脚本。
3.1 三种脚本构建方式对比
| 方式 | 适用场景 | 优点 | 缺点 | 学习成本 |
|---|---|---|---|---|
| HTTP代理服务器录制 | Web/移动端HTTP(S)请求 | 零编码、快速捕获请求序列 | 易混入无关请求、需手动过滤 | 低 |
| Badboy录制 | 带AJAX/复杂交互的Web应用 | 可视化操作、自动关联动态参数 | 工具停止维护、仅限Windows | 中 |
| 手动编写脚本 | API接口、非HTTP协议、参数化复杂场景 | 完全可控、无冗余、可复用性强 | 需熟悉JMeter元件与协议细节 | 高 |
选型建议: 首次接触新系统时,优先使用HTTP代理服务器录制快速生成骨架脚本;对于含大量异步请求的富客户端应用,可尝试Badboy录制;当需要精确控制请求参数、断言逻辑或处理非HTTP协议时,必须采用手动编写方式。
3.2 HTTP代理服务器录制
JMeter内置的HTTP代理服务器是最常用的录制方式。其原理是:JMeter在本地启动一个代理端口,浏览器或移动端将流量指向该代理,JMeter拦截并记录所有HTTP(S)请求,自动生成对应的Sampler。
3.2.1 配置步骤
- 添加HTTP代理服务器: 右键“测试计划” → 添加 → 非测试元件 → HTTP代理服务器。
- 设置端口: 默认8888,确保未被占用。
- 目标控制器: 选择录制结果存放的线程组。
- 分组策略: 建议选择“在每个线程组的新控制器中分组”,便于后续按页面/功能模块整理。
- 请求过滤: 在“包含模式”/“排除模式”中添加正则,过滤掉静态资源(.css、.js、.png、.ico等)和第三方统计请求。
- 启动代理: 点击“启动”按钮,JMeter会弹出证书提示(录制HTTPS时需要安装根证书)。
- 配置浏览器代理: 将浏览器HTTP/HTTPS代理指向127.0.0.1:8888。
- 执行操作: 在浏览器中完成业务操作,JMeter实时捕获请求。
- 停止录制: 点击“停止”按钮,移除浏览器代理设置。
避坑要点: 录制HTTPS站点时,必须先在浏览器中导入JMeter生成的ApacheJMeterTemporaryRootCA.crt证书,否则请求会失败。录制完成后务必删除该证书,避免安全风险。
3.2.2 录制后脚本清洗
原始录制脚本通常包含大量冗余请求,必须进行清洗才能用于压测:
- 删除静态资源: 图片、CSS、JS、字体文件等非业务核心请求。
- 合并请求: 将连续的同域名请求合并为一次,减少网络往返。
- 参数化: 将登录态、订单号、用户ID等动态值替换为变量。
- 添加断言: 为关键请求添加响应断言,确保业务成功。
- 添加思考时间: 使用Uniform Random Timer模拟用户操作间隔。
3.3 Badboy录制
Badboy是一款独立的Web测试录制工具,支持录制、回放和导出为JMeter脚本。虽然已停止更新,但在处理AJAX、动态页面元素方面仍有独特优势。
3.3.1 录制流程
- 打开Badboy,在地址栏输入被测系统URL。
- 点击红色“录制”按钮,开始操作。
- Badboy自动记录所有请求、Cookie、Session信息。
- 录制完成后,点击“文件” → “导出” → “导出为JMeter脚本”。
- 在JMeter中打开导出的.jmx文件,进行参数化和断言调整。
Badboy vs HTTP代理服务器: Badboy能自动识别页面中的动态参数(如ViewState、EventValidation),并生成对应的正则表达式提取器,减少手动关联工作量。但Badboy不支持HTTP/2协议,且对现代SPA应用(如Vue、React)的录制效果较差。
3.4 手动编写脚本基础
手动编写脚本是性能测试工程师的核心能力。它不依赖录制工具,完全通过JMeter元件构建请求,适合接口测试、非HTTP协议场景以及需要精细控制的压测脚本。
3.4.1 手动编写核心元件
| 元件类型 | 作用 | 常用实现 |
|---|---|---|
| 线程组 | 定义虚拟用户数、Ramp-Up时间、循环次数 | 线程组、setUp线程组、tearDown线程组 |
| Sampler | 发送具体请求 | HTTP请求、JDBC请求、Java请求、SOAP/XML-RPC请求 |
| 配置元件 | 管理请求默认值、参数化数据 | HTTP请求默认值、CSV数据文件设置、用户定义的变量 |
| 前置处理器 | 在请求前执行逻辑 | 用户参数、JDBC前置处理器、JSR223前置处理器 |
| 后置处理器 | 从响应中提取数据 | 正则表达式提取器、JSON提取器、边界提取器 |
| 断言 | 验证响应是否符合预期 | 响应断言、JSON断言、大小断言 |
| 监听器 | 查看和保存测试结果 | 查看结果树、聚合报告、图形结果 |
3.4.2 手动编写示例:登录接口
以下是一个手动编写的HTTP登录请求脚本片段:
<!-- 线程组配置 -->
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="登录压测" enabled="true">
<stringProp name="ThreadGroup.num_threads">10</stringProp>
<stringProp name="ThreadGroup.ramp_time">5</stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">false</boolProp>
</ThreadGroup>
<!-- CSV数据文件配置 -->
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="用户数据" enabled="true">
<stringProp name="filename">users.csv</stringProp>
<stringProp name="variableNames">username,password</stringProp>
<boolProp name="quotedData">false</boolProp>
<boolProp name="recycle">true</boolProp>
<boolProp name="stopThread">false</boolProp>
</CSVDataSet>
<!-- HTTP请求 -->
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="登录请求" enabled="true">
<stringProp name="HTTPSampler.domain">api.example.com</stringProp>
<stringProp name="HTTPSampler.port">443</stringProp>
<stringProp name="HTTPSampler.protocol">https</stringProp>
<stringProp name="HTTPSampler.path">/api/v1/login</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<stringProp name="HTTPSampler.arguments">
{"username":"${username}","password":"${password}"}
</stringProp>
</HTTPSamplerProxy>
<!-- JSON提取器 -->
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="提取" enabled="true">
<stringProp name="JSONPostProcessor.referenceNames"></stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.data.</stringProp>
<stringProp name="JSONPostProcessor.match_numbers">0</stringProp>
</JSONPostProcessor>
<!-- 响应断言 -->
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="登录成功断言" enabled="true">
<stringProp name="Assertion.test_field">ASSERTION_TEXT_RESPONSE</stringProp>
<stringProp name="Assertion.test_string">"code":200</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
</ResponseAssertion>
3.5 脚本录制与回放流程总览
下图展示了从录制/编写到回放验证的完整工作流:
3.6 三种方式的最佳实践
实战建议:
- 混合使用: 先用HTTP代理录制获取请求骨架,再手动调整参数化、关联和断言逻辑。
- 录制前规划: 明确要录制的业务路径,避免录制无关操作增加清洗工作量。
- 手动编写优先: 对于核心接口(登录、下单、支付),建议手动编写脚本,确保完全可控。
- 版本控制: 所有.jmx脚本纳入Git管理,录制脚本和手动脚本分开存放。
3.7 常见问题与排查
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 录制时浏览器无法打开页面 | 代理端口被占用或证书未安装 | 更换端口;重新安装JMeter根证书 |
| 录制结果为空 | 浏览器未配置代理或过滤规则过严 | 检查代理设置;放宽包含模式 |
| Badboy导出脚本乱码 | 编码不匹配 | 在Badboy中设置UTF-8编码后重新导出 |
| 手动脚本请求失败 | 缺少Header或Cookie | 添加HTTP信息头管理器;检查Cookie管理器配置 |
| 回放时断言失败 | 动态参数未关联或响应格式变化 | 使用后置处理器提取动态值;更新断言表达式 |