前言
JMeter 是一款功能强大的开源性能测试工具,在接口测试方面具备诸多优势:
- 支持多种协议:能够模拟 HTTP/HTTPS、FTP、JDBC、SOAP、XML-RPC 等多种协议的请求。例如,在测试基于 RESTful 架构的 Web 服务时,通过 HTTP Request 取样器,可以方便地发送 GET、POST、PUT、DELETE 等请求 ,并设置请求头、请求参数等信息。对于数据库相关接口,可利用 JDBC Request 取样器发送 SQL 查询请求,验证数据的增删改查功能以及性能表现。
- 参数化与数据驱动:支持参数化测试,允许从 CSV 文件、数据库等数据源读取数据,将其作为请求参数进行多次测试。比如,测试用户注册接口时,可以从 CSV 文件中读取不同的用户名、密码等数据,模拟大量用户注册的场景,检查接口的健壮性和性能。
- 性能测试能力:通过线程组可以灵活设置并发用户数、请求的启动速度、循环次数等参数,模拟不同规模的用户并发访问,测试接口在高负载情况下的响应时间、吞吐量、错误率等性能指标。同时,结合监听器,如 Aggregate Report(聚合报告)、Graph Results(图形结果)等,能够直观地展示接口的性能数据,帮助测试人员分析和定位性能瓶颈。
- 断言机制:可以添加各种断言,如响应断言、JSON 断言、XML 断言等,用于验证接口返回的响应是否符合预期。例如,检查响应状态码是否为 200,响应内容中是否包含特定的字段或值,确保接口功能的正确性。
JMeter 不适合用于测试画面 UI ,主要原因如下:
- 缺乏 UI 交互模拟能力:JMeter 专注于协议层的请求和响应,它无法模拟用户在 UI 界面上的实际操作,如点击按钮、输入文本、滑动屏幕、选择下拉框等。对于 UI 元素的定位、操作和验证,JMeter 没有相应的内置功能。
- 无法处理 UI 展示效果:无法验证 UI 的布局是否正确、元素的显示是否完整、颜色和字体是否符合设计要求等展示层面的问题。例如,判断按钮的大小和位置是否合适、文本是否清晰可读、图片是否正常显示等,这些都是 JMeter 无法做到的。
- 不支持 UI 事件触发:在实际使用中,UI 界面会有各种事件触发逻辑,如鼠标悬停显示提示信息、点击按钮弹出对话框等。JMeter 无法模拟这些 UI 事件,也就无法测试与之相关的功能。
如果需要测试画面 UI,可以使用专门的 UI 自动化测试工具,如 Selenium、Appium 等。Selenium 主要用于 Web 应用的 UI 测试,Appium 则适用于移动应用(iOS 和 Android)的 UI 自动化测试 。
定义Test Plan
在 JMeter 中,Test Plan(测试计划) 是整个性能测试 / 接口测试的顶层容器,相当于一个 “测试项目的总配置文件”,包含了所有测试相关的组件、逻辑和全局设置。所有的测试内容(线程组、取样器、监听器等)都必须放在 Test Plan 之下,它定义了测试的整体流程、资源和执行规则。
Test Plan 的核心作用
- 组织测试组件
作为最顶层的容器,Test Plan 包含了测试所需的所有元素:线程组(虚拟用户配置)、取样器(请求定义)、配置元件(参数 / 连接配置)、断言(响应验证)、监听器(结果收集)等。这些组件按层级关系排列,形成完整的测试逻辑。 - 定义全局配置
提供全局级别的设置,影响整个测试的执行,例如:- 全局变量(User Defined Variables):可在所有组件中复用的变量(如服务器地址、端口号)。
- 线程组执行顺序:控制多个线程组是并行执行还是按顺序执行。
- 类路径扩展:引入自定义 Java 库或脚本(如加密工具类)。
- 统一测试逻辑
所有测试步骤和规则都在 Test Plan 中集中管理,确保测试流程的一致性(如 “先执行登录请求,再执行查询请求” 的逻辑)。
一个典型的 Test Plan 结构如下:
Test Plan(测试计划) ├─ Thread Group(线程组:定义虚拟用户) │ ├─ HTTP Request(取样器:具体请求) │ ├─ HTTP Cookie Manager(配置元件:管理 Cookie) │ ├─ Response Assertion(断言:验证响应) │ └─ View Results Tree(监听器:查看结果) ├─ Setup Thread Group(前置线程组:初始化操作) └─ Teardown Thread Group(后置线程组:清理操作)

Run Thread Groups consecutively (i.e. one at a time) 勾选后,线程组按顺序执行(先执行完第一个线程组,再执行第二个); 不勾选则并行执行(所有线程组同时启动)。 → 适合需要 “先初始化数据(第一个线程组),再压测(第二个线程组)” 的场景。 Run tearDown Thread Groups after shutdown of main threads 勾选后,主线程(普通线程组)执行完毕后,才会执行 tearDown Thread Group(用于清理资源的线程组); 不勾选则 tearDown 与主线程并行执行。 → 通常建议勾选,确保 “清理操作” 在主流程结束后执行。 Functional Test Mode (i.e. save Response Data and Sampler Data) 勾选后,JMeter 会保存所有请求的响应数据(如服务器返回的 HTML/JSON)和取样器元数据(如请求参数、 headers)。 → 注意:开启后会大幅增加内存 / 磁盘占用(尤其高并发场景),仅建议在调试阶段勾选,性能测试时一般关闭。
定义线程组


当线程组中的取样器(如 HTTP 请求)执行失败时,JMeter 如何处理:
选项 | 作用 |
---|---|
Continue | 忽略错误,继续执行后续取样器(默认选项,适合 “允许部分失败” 的场景)。 |
Start Next Thread Loop | 停止当前线程的循环,直接开始下一轮循环(若有 Loop Count > 1)。 |
Stop Thread | 停止当前线程(仅当前虚拟用户停止,其他线程继续执行)。 |
Stop Test | 停止整个测试计划,但需等待当前正在执行的取样器完成。 |
Stop Test Now | 立即强制停止整个测试计划(不等待当前取样器完成,可能导致数据不完整)。 |

Thread Properties(线程属性:核心配置) 定义 “虚拟用户的数量、启动速度、循环次数”,是线程组最关键的部分: Number of Threads (users) 并发用户数(虚拟用户数量),即同时模拟多少个用户执行该线程组的请求。 → 示例:设为 10,则同时有 10 个虚拟用户执行请求。 Ramp-up period (seconds) 线程启动的 “总时长”(秒),控制虚拟用户的启动速度。 → 公式:每个线程的启动间隔 = Ramp-up / Number of Threads → 示例:10 个线程,Ramp-up 设为 5 → 每 0.5 秒启动 1 个线程(5/10=0.5)。 → 若设为 0,则所有线程瞬间同时启动(适合模拟 “突发高并发”)。 Loop Count 每个线程执行请求的循环次数: 勾选 Infinite:无限循环(需配合 “Duration” 或手动停止)。 输入数字(如 3):每个线程执行 3 次请求。 Same user on each iteration 勾选后,线程在多次循环中保持相同的 “用户身份”(如 Cookie、Session 不变); 不勾选则每次循环重新初始化(适合模拟 “不同用户重复操作”)。 Delay Thread creation until needed 延迟创建线程,直到需要时才初始化(节省内存,适合线程数多但启动慢的场景)。 Specify Thread lifetime 手动控制线程的 “存活时间” 和 “启动延迟”: Duration (seconds) 线程执行的总时长(覆盖 Loop Count,到时间后自动停止)。 Startup delay (seconds) 线程启动前的延迟时间(如让线程组 2 在 10 秒后启动)。
核心逻辑总结:线程组的配置决定了 “虚拟用户如何工作”:
- 并发数(Number of Threads) → 模拟多少用户。
- 启动速度(Ramp-up) → 模拟用户是 “瞬间涌入” 还是 “缓慢增加”。
- 循环次数(Loop Count) → 每个用户重复操作多少次。
- 错误处理 → 遇到失败时是 “继续执行” 还是 “终止测试”。
可以看到如果按照下面的图片设定,10个线程,30秒内启动,这样的话,Jmeter会每3秒启动一个线程。

2025-08-07 12:59:30,575 INFO o.a.j.e.StandardJMeterEngine: Running the test! 2025-08-07 12:59:30,576 INFO o.a.j.s.SampleEvent: List of sample_variables: [] 2025-08-07 12:59:30,576 INFO o.a.j.g.u.JMeterMenuBar: setRunning(true, *local*) 2025-08-07 12:59:30,656 INFO o.a.j.e.StandardJMeterEngine: Starting ThreadGroup: 1 : 根据lastName查询Owner信息 2025-08-07 12:59:30,656 INFO o.a.j.e.StandardJMeterEngine: Starting 10 threads for group 根据lastName查询Owner信息. 2025-08-07 12:59:30,656 INFO o.a.j.e.StandardJMeterEngine: Thread will continue on error 2025-08-07 12:59:30,656 INFO o.a.j.t.ThreadGroup: Starting thread group... number=1 threads=10 ramp-up=30 delayedStart=false 2025-08-07 12:59:30,657 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-1 2025-08-07 12:59:30,658 INFO o.a.j.t.ThreadGroup: Started thread group number 1 2025-08-07 12:59:30,658 INFO o.a.j.e.StandardJMeterEngine: All thread groups have been started 2025-08-07 12:59:30,733 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-1 2025-08-07 12:59:30,733 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-1 2025-08-07 12:59:33,657 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-2 2025-08-07 12:59:33,732 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-2 2025-08-07 12:59:33,732 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-2 2025-08-07 12:59:36,658 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-3 2025-08-07 12:59:36,736 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-3 2025-08-07 12:59:36,736 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-3 2025-08-07 12:59:39,657 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-4 2025-08-07 12:59:39,730 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-4 2025-08-07 12:59:39,731 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-4 2025-08-07 12:59:42,656 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-5 2025-08-07 12:59:42,730 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-5 2025-08-07 12:59:42,731 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-5 2025-08-07 12:59:45,658 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-6 2025-08-07 12:59:45,738 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-6 2025-08-07 12:59:45,738 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-6 2025-08-07 12:59:48,658 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-7 2025-08-07 12:59:48,734 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-7 2025-08-07 12:59:48,734 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-7 2025-08-07 12:59:51,658 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-8 2025-08-07 12:59:51,734 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-8 2025-08-07 12:59:51,734 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-8 2025-08-07 12:59:54,657 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-9 2025-08-07 12:59:54,737 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-9 2025-08-07 12:59:54,737 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-9 2025-08-07 12:59:57,657 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-10 2025-08-07 12:59:57,737 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-10 2025-08-07 12:59:57,737 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-10 2025-08-07 12:59:57,739 INFO o.a.j.e.StandardJMeterEngine: Notifying test listeners of end of test 2025-08-07 12:59:57,740 INFO o.a.j.g.u.JMeterMenuBar: setRunning(false, *local*)
可以用上面的🧹图标,清除控制台的Log输出

我们把Ramp-up 设置成0,看下效果。

2025-08-07 13:15:57,396 INFO o.a.j.e.StandardJMeterEngine: Running the test! 2025-08-07 13:15:57,397 INFO o.a.j.s.SampleEvent: List of sample_variables: [] 2025-08-07 13:15:57,398 INFO o.a.j.g.u.JMeterMenuBar: setRunning(true, *local*) 2025-08-07 13:15:57,459 INFO o.a.j.e.StandardJMeterEngine: Starting ThreadGroup: 1 : 根据lastName查询Owner信息 2025-08-07 13:15:57,459 INFO o.a.j.e.StandardJMeterEngine: Starting 10 threads for group 根据lastName查询Owner信息. 2025-08-07 13:15:57,459 INFO o.a.j.e.StandardJMeterEngine: Thread will continue on error 2025-08-07 13:15:57,459 INFO o.a.j.t.ThreadGroup: Starting thread group... number=1 threads=10 ramp-up=0 delayedStart=false 2025-08-07 13:15:57,460 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-1 2025-08-07 13:15:57,460 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-3 2025-08-07 13:15:57,460 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-4 2025-08-07 13:15:57,460 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-2 2025-08-07 13:15:57,460 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-5 2025-08-07 13:15:57,460 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-6 2025-08-07 13:15:57,461 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-7 2025-08-07 13:15:57,461 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-8 2025-08-07 13:15:57,461 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-9 2025-08-07 13:15:57,461 INFO o.a.j.t.ThreadGroup: Started thread group number 1 2025-08-07 13:15:57,461 INFO o.a.j.e.StandardJMeterEngine: All thread groups have been started 2025-08-07 13:15:57,462 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-10 2025-08-07 13:15:57,585 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-3 2025-08-07 13:15:57,585 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-3 2025-08-07 13:15:57,585 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-5 2025-08-07 13:15:57,585 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-5 2025-08-07 13:15:57,586 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-8 2025-08-07 13:15:57,586 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-8 2025-08-07 13:15:57,592 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-1 2025-08-07 13:15:57,592 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-1 2025-08-07 13:15:57,601 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-9 2025-08-07 13:15:57,601 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-9 2025-08-07 13:15:57,601 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-6 2025-08-07 13:15:57,601 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-6 2025-08-07 13:15:57,601 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-7 2025-08-07 13:15:57,603 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-7 2025-08-07 13:15:57,605 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-4 2025-08-07 13:15:57,605 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-2 2025-08-07 13:15:57,605 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-4 2025-08-07 13:15:57,605 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-2 2025-08-07 13:15:57,610 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-10 2025-08-07 13:15:57,610 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-10 2025-08-07 13:15:57,610 INFO o.a.j.e.StandardJMeterEngine: Notifying test listeners of end of test 2025-08-07 13:15:57,610 INFO o.a.j.g.u.JMeterMenuBar: setRunning(false, *local*)
从log上看,可以看到10个测试线程是一瞬间启动了,这样可以模拟用户瞬间涌入的动作。
这些配置直接影响测试的压力模型(如 “100 用户瞬间并发” vs “100 用户 10 秒内逐步启动”),需根据实际场景(如秒杀、日常访问)调整。
Timer
(定时器)
在 JMeter 中,Timer
(定时器)用于控制取样器(如 HTTP Request )执行的时间间隔,模拟用户在操作之间的等待时间,使测试更接近真实用户行为,还能用于调整请求的发送频率来模拟不同的负载场景 。以下是图中几种常见定时器的作用:
Constant Timer(固定定时器)
- 作用:在每个取样器执行前,等待固定的时间(单位为毫秒)。比如设置为
500
毫秒,那么每个相关取样器执行前都会等待500
毫秒,常用于模拟用户稳定的思考时间或操作间隔 。 - 应用场景:模拟用户在浏览网页时,查看页面内容所花费的稳定时间 。例如,用户在查看商品详情页时,平均会停留 3 秒,就可以对商品详情页请求添加 Constant Timer 并设置等待时间为
3000
毫秒。
Synchronizing Timer (集合点)
- 作用:也被称为 “集合点” 定时器,用于模拟大量用户同时并发访问的场景。它会让线程等待,直到指定数量的线程到达集合点,然后一起释放,同时发送请求,模拟高并发瞬间压力。
- 应用场景:常用于性能测试中的峰值压力测试。比如在电商秒杀活动中,模拟大量用户在同一时刻点击抢购按钮的场景 ,通过设置集合点线程数为实际参与秒杀的用户数,来测试系统在高并发下的响应能力。
Constant Timer(固定定时器)的例子:
可以看到Jmeter马上启动了10个线程,但是没有立即发送Http请求,在过去10秒以后,才发送的请求。


2025-08-07 13:42:06,331 INFO o.a.j.e.StandardJMeterEngine: Running the test! 2025-08-07 13:42:06,332 INFO o.a.j.s.SampleEvent: List of sample_variables: [] 2025-08-07 13:42:06,333 INFO o.a.j.g.u.JMeterMenuBar: setRunning(true, *local*) 2025-08-07 13:42:06,400 INFO o.a.j.e.StandardJMeterEngine: Starting ThreadGroup: 1 : 根据lastName查询Owner信息 2025-08-07 13:42:06,400 INFO o.a.j.e.StandardJMeterEngine: Starting 10 threads for group 根据lastName查询Owner信息. 2025-08-07 13:42:06,400 INFO o.a.j.e.StandardJMeterEngine: Thread will continue on error 2025-08-07 13:42:06,400 INFO o.a.j.t.ThreadGroup: Starting thread group... number=1 threads=10 ramp-up=0 delayedStart=false 2025-08-07 13:42:06,401 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-1 2025-08-07 13:42:06,401 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-2 2025-08-07 13:42:06,401 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-3 2025-08-07 13:42:06,402 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-4 2025-08-07 13:42:06,402 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-5 2025-08-07 13:42:06,402 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-6 2025-08-07 13:42:06,402 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-7 2025-08-07 13:42:06,403 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-8 2025-08-07 13:42:06,403 INFO o.a.j.t.ThreadGroup: Started thread group number 1 2025-08-07 13:42:06,403 INFO o.a.j.e.StandardJMeterEngine: All thread groups have been started 2025-08-07 13:42:06,403 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-10 2025-08-07 13:42:06,403 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-9 2025-08-07 13:42:16,527 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-2 2025-08-07 13:42:16,527 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-2 2025-08-07 13:42:16,533 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-10 2025-08-07 13:42:16,533 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-10 2025-08-07 13:42:16,539 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-5 2025-08-07 13:42:16,539 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-5 2025-08-07 13:42:16,543 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-7 2025-08-07 13:42:16,543 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-7 2025-08-07 13:42:16,547 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-6 2025-08-07 13:42:16,547 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-6 2025-08-07 13:42:16,548 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-8 2025-08-07 13:42:16,548 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-8 2025-08-07 13:42:16,552 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-3 2025-08-07 13:42:16,552 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-1 2025-08-07 13:42:16,552 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-4 2025-08-07 13:42:16,552 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-1 2025-08-07 13:42:16,553 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-9 2025-08-07 13:42:16,553 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-9 2025-08-07 13:42:16,552 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-4 2025-08-07 13:42:16,552 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-3 2025-08-07 13:42:16,557 INFO o.a.j.e.StandardJMeterEngine: Notifying test listeners of end of test 2025-08-07 13:42:16,557 INFO o.a.j.g.u.JMeterMenuBar: setRunning(false, *local*)
Synchronizing Timer 的例子:
可以看到10个线程,60秒内启动,当每启动3个线程时,会一起发送一次请求。


2025-08-07 13:48:49,226 INFO o.a.j.e.StandardJMeterEngine: Running the test! 2025-08-07 13:48:49,227 INFO o.a.j.s.SampleEvent: List of sample_variables: [] 2025-08-07 13:48:49,228 INFO o.a.j.g.u.JMeterMenuBar: setRunning(true, *local*) 2025-08-07 13:48:49,297 INFO o.a.j.e.StandardJMeterEngine: Starting ThreadGroup: 1 : 根据lastName查询Owner信息 2025-08-07 13:48:49,297 INFO o.a.j.e.StandardJMeterEngine: Starting 10 threads for group 根据lastName查询Owner信息. 2025-08-07 13:48:49,297 INFO o.a.j.e.StandardJMeterEngine: Thread will continue on error 2025-08-07 13:48:49,297 INFO o.a.j.t.ThreadGroup: Starting thread group... number=1 threads=10 ramp-up=60 delayedStart=false 2025-08-07 13:48:49,297 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-1 2025-08-07 13:48:49,300 INFO o.a.j.t.ThreadGroup: Started thread group number 1 2025-08-07 13:48:49,300 INFO o.a.j.e.StandardJMeterEngine: All thread groups have been started 2025-08-07 13:48:55,299 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-2 2025-08-07 13:49:01,299 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-3 2025-08-07 13:49:01,379 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-1 2025-08-07 13:49:01,379 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-1 2025-08-07 13:49:01,385 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-2 2025-08-07 13:49:01,386 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-2 2025-08-07 13:49:01,388 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-3 2025-08-07 13:49:01,388 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-3 2025-08-07 13:49:07,299 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-4 2025-08-07 13:49:13,299 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-5 2025-08-07 13:49:19,299 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-6 2025-08-07 13:49:19,385 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-5 2025-08-07 13:49:19,385 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-6 2025-08-07 13:49:19,385 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-5 2025-08-07 13:49:19,385 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-6 2025-08-07 13:49:19,388 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-4 2025-08-07 13:49:19,388 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-4 2025-08-07 13:49:25,299 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-7 2025-08-07 13:49:31,300 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-8 2025-08-07 13:49:37,299 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-9 2025-08-07 13:49:37,382 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-8 2025-08-07 13:49:37,382 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-8 2025-08-07 13:49:37,383 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-9 2025-08-07 13:49:37,383 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-9 2025-08-07 13:49:37,385 INFO o.a.j.t.JMeterThread: Thread is done: 根据lastName查询Owner信息 1-7 2025-08-07 13:49:37,385 INFO o.a.j.t.JMeterThread: Thread finished: 根据lastName查询Owner信息 1-7 2025-08-07 13:49:43,299 INFO o.a.j.t.JMeterThread: Thread started: 根据lastName查询Owner信息 1-10
Assertion
(断言)
在 JMeter 中,Assertion 即断言,是一种用于验证取样器(如 HTTP Request)返回的响应是否符合预期的组件 。它能确保测试过程中,系统返回的结果是正确的,进而判断系统功能是否正常实现。以下是关于断言的详细介绍:
断言的作用
- 验证功能正确性:通过设置断言条件,判断接口返回的数据是否符合预期的业务逻辑。例如,对于登录接口,断言可以验证返回的状态码是否为 200,以及响应体中是否包含表示登录成功的特定字段(如 token),以此来确认登录功能是否正常。
- 发现错误结果:在性能测试或者功能测试中,及时发现不符合预期的响应。如果断言失败,说明请求返回的结果与预期不一致,可能存在系统缺陷,如接口逻辑错误、数据错误等。比如在查询订单接口测试中,断言预期返回的订单数量与实际数量是否一致,若不一致则说明可能存在查询逻辑问题或者数据异常。
- 辅助测试分析:帮助测试人员快速定位问题。当测试结果出现异常时,可以通过断言失败的信息,迅速判断是哪个请求、哪个条件没有满足,从而缩小问题排查的范围,提高测试效率。
常见的断言类型
- 响应断言(Response Assertion):最常用的断言类型。可以对响应的多种属性进行验证,包括响应文本(判断响应体中是否包含或不包含特定字符串)、响应代码(如 HTTP 状态码是否为预期值)、响应信息(如 HTTP 响应中的状态信息,如 “OK”)、响应头(验证响应头中某个字段的值是否正确)等。例如,测试一个获取商品详情的接口时,使用响应断言检查响应文本中是否包含商品名称等关键信息。
- JSON 断言(JSON Assertion):专门用于验证 JSON 格式的响应数据。可以验证 JSON 响应中的某个字段值是否正确,或者检查某个路径下的数据结构是否符合预期。比如在测试一个返回 JSON 数据的用户信息接口时,使用 JSON 断言验证用户的年龄、性别等字段是否正确。
- XML 断言(XML Assertion):针对 XML 格式的响应进行验证,与 JSON 断言类似,用于检查 XML 文档中的节点、属性值等是否符合预期。比如在测试一个基于 XML 传输数据的 Web 服务接口时,验证 XML 响应中订单编号、金额等节点的值是否正确。
- 大小断言(Size Assertion):用于验证响应的大小(字节数)是否在指定的范围内。例如,限制某个页面响应的大小不能超过一定的字节数,以确保页面资源没有过度加载。
- Duration Assertion(持续时间断言):验证取样器执行所花费的时间是否在预期范围内。比如要求某个接口的响应时间不能超过 500 毫秒,若超过则断言失败,这对于性能测试中评估接口响应速度很有帮助。
断言的使用方法
- 添加断言:右键点击取样器(如 HTTP Request),选择 “Add” -> “Assertions”,然后从多种断言类型中选择需要的断言添加到取样器下。
- 配置断言条件:添加断言后,根据实际需求配置断言条件。例如,对于响应断言,需要设置要验证的响应属性(如响应文本)、匹配规则(包含、不包含、等于等)以及要验证的具体内容(如特定字符串)。
- 查看断言结果:运行测试计划后,通过监听器(如 View Results Tree)查看断言结果。如果断言成功,取样器显示为绿色;如果断言失败,取样器显示为红色,并可以在监听器中查看具体的失败信息,以便进行问题分析和定位。
断言是 JMeter 中确保测试结果准确性和可靠性的重要工具,在功能测试和性能测试中都起着关键作用。
添加一个断言,验证Response Code 是否等于200



把验证Code 改成300,再次执行,看下结果。可以在View Results Tree看到Http请求,断言的结果失败了。


Jmeter组件ーCSV数据文件设置
在 JMeter 中,CSV Data Set Config(CSV 数据集配置元件)是一个常用的配置元件,主要用于实现数据驱动测试 。它允许从 CSV(逗号分隔值)文件中读取数据,并将这些数据提供给测试计划中的取样器(如 HTTP Request),实现测试数据的参数化,以下是详细介绍:
主要作用
- 参数化测试数据:在测试过程中,很多时候需要使用不同的输入数据来验证系统的功能和性能。例如,测试用户注册接口时,需要使用不同的用户名、密码、邮箱等数据进行多次测试。CSV Data Set Config 可以将这些测试数据事先整理到 CSV 文件中,然后在测试时从文件中逐行读取数据,提供给注册接口的请求参数,从而实现快速、批量的测试,避免手动逐个设置参数的繁琐操作。
- 模拟真实场景:通过使用包含大量真实数据的 CSV 文件,可以更真实地模拟用户在实际使用系统时的各种情况。比如在测试电商系统的搜索功能时,从包含众多商品关键词的 CSV 文件中读取数据,模拟不同用户的搜索行为,有助于更全面地发现系统可能存在的问题。
- 提高测试脚本复用性:当需要测试同一接口在不同数据输入下的表现时,只需更换 CSV 文件中的数据,而无需修改测试脚本的主体逻辑,大大提高了测试脚本的复用性和可维护性。
配置项说明
Filename(文件名): 指定要读取的 CSV 文件的路径。可以输入绝对路径(如C:/testdata/users.csv ),也可以输入相对路径(相对于 JMeter 启动目录的路径)。 File encoding(文件编码): 设置 CSV 文件的编码格式,常见的有 UTF-8、GBK 等。确保选择的编码格式与实际 CSV 文件的编码一致,以避免读取数据时出现乱码问题。 Variable Names (comma-delimited)(变量名,以逗号分隔): 定义从 CSV 文件中读取的数据对应的变量名。变量名之间用逗号分隔,顺序要与 CSV 文件中列的顺序一致。 例如,CSV 文件第一列是用户名,第二列是密码,那么可以设置变量名为 username,password ,后续在测试计划中就可以通过 ${username} 和 ${password} 来引用这些变量。 Delimiter(分隔符): 指定 CSV 文件中数据的分隔符,默认是逗号(,)。如果 CSV 文件使用其他分隔符,如制表符(\t),则需要相应地进行设置。 Allow quoted data?(允许引用数据吗?): 如果 CSV 文件中的某些字段包含分隔符,通常会使用引号(如双引号 " )将该字段括起来,这时需要勾选此选项,JMeter 才能正确识别和处理这些数据。 Recycle on EOF?(到达文件末尾时循环?): 如果勾选此项,当读取到 CSV 文件末尾时,会重新从文件开头读取数据;如果不勾选,到达文件末尾后,后续读取操作将返回空值,导致测试可能失败。 Stop Thread on EOF?(到达文件末尾时停止线程?): 若勾选,当读取到 CSV 文件末尾且不循环时,所在线程将停止执行;若不勾选,线程会继续执行后续操作,只是获取到的数据可能不符合预期。 Share mode(共享模式): All threads:所有线程共享同一个文件读取指针,即多个线程按顺序依次读取 CSV 文件中的数据。例如,线程 1 读取第一行,线程 2 读取第二行,以此类推。 Current thread group:当前线程组内的线程共享文件读取指针,不同线程组之间独立读取。 Current thread:每个线程独立读取 CSV 文件,每个线程都有自己的文件读取指针,会重复读取文件中的数据。

这里准备一个lastName的CSV文件

定义 CSV Data Set Config,读取CSV文件,并且定义csv文件的参数

在Http请求这里,换成参数写法。

这里启动5个线程,会读取CSV文件的lastName

查看下执行结果,可以看到每次Http请求的参数都不一样了,都是从CSV文件中依次读取的数据。


