接口自动化测试实战:从 HTTP 基础到 JMeter + Newman 全链路覆盖

接口自动化测试实战:从 HTTP 基础到 JMeter + Newman 全链路覆盖

环境说明

节点 公网 IP 私网 IP 规格 用途
ecs-916d-0001 121.36.51.148 192.168.0.178 2C4G Ubuntu 24.04 被测系统(悦购图书商城 REST API)
ecs-916d-0002 119.3.175.121 192.168.0.246 2C4G Ubuntu 24.04 Newman 命令行执行节点
ecs-916d-0003 1.92.93.183 192.168.0.66 2C4G Ubuntu 24.04 JMeter 5.6.3 测试节点
ecs-916d-0004 120.46.82.41 192.168.0.222 2C4G Ubuntu 24.04 Ant 报告生成节点

一、接口测试基础概念

1.1 什么是接口测试

接口(API)是系统组件之间通信的契约,规定了"我提供什么数据、你需要传什么参数、我返回什么格式"。接口测试就是绕开 UI,直接对这层契约进行验证。

复制代码
传统 UI 测试路径:
  测试人员 → 浏览器/App → UI 层 → 业务层 → 数据层

接口测试路径:
  测试人员 → HTTP 请求 → 业务层 → 数据层
              (绕过 UI,直达后端)

接口测试的核心价值:

  • 发现更早:UI 还没做好时接口就可以测
  • 覆盖更深:能构造 UI 无法触发的异常入参
  • 运行更快:无渲染开销,秒级执行
  • 定位更准:响应报文直接暴露后端逻辑问题

1.2 接口测试的分类

分类维度 类型 说明
协议 HTTP/HTTPS 最常见,Web 服务主流协议
协议 RPC / gRPC 微服务内部调用,高性能
协议 WebSocket 长连接,实时推送场景
协议 GraphQL 前端自定义查询结构
场景 单接口测试 验证单个 API 的功能正确性
场景 场景链路测试 多接口依赖串联,如登录→下单→查询
场景 边界/异常测试 空参数、超长字段、越权访问等

1.3 HTTP 协议核心要素

请求报文结构
复制代码
POST /api/user/login HTTP/1.1           ← 请求行(方法 路径 版本)
Host: 121.36.51.148:8080                ← 请求头(Headers)
Content-Type: application/json
Authorization: Bearer abc123token
                                         ← 空行(分隔头和体)
{"email":"test@example.com","password":"123456"}  ← 请求体(Body)
常用 HTTP 方法对照
方法 语义 幂等 典型场景
GET 查询资源 获取图书列表、详情
POST 创建资源 用户登录、提交订单
PUT 全量更新 更新用户信息
PATCH 部分更新 修改单个字段
DELETE 删除资源 删除记录
响应状态码速查
范围 含义 常见码
2xx 成功 200 OK、201 Created、204 No Content
3xx 重定向 301 永久、302 临时
4xx 客户端错误 400 参数错误、401 未授权、403 禁止、404 不存在
5xx 服务端错误 500 内部错误、502 网关错误、503 服务不可用

1.4 接口测试用例设计思路

接口测试用例需覆盖以下维度:

  1. 正常场景:按文档预期的正确入参,验证返回 2xx 和业务数据正确
  2. 边界值:最大/最小长度、最大/最小数值、边界时间戳
  3. 异常参数:空值、null、错误类型(数字传字符串)
  4. 权限验证:无 Token、过期 Token、越权访问
  5. 业务规则:库存不足下单、重复注册、金额精度

二、被测系统:悦购图书商城 REST API

2.1 系统部署(itest-01: 121.36.51.148)

在 ecs-916d-0001 上部署 Flask REST API 作为被测系统:

bash 复制代码
# 安装依赖
apt-get install -y python3 python3-pip
pip3 install 'flask>=3.0' --break-system-packages --ignore-installed

# 查看安装结果
root@ecs-916d-0001:~# python3 -c "import flask; print(flask.__version__)"
3.1.3

API 服务提供以下接口(运行在 8080 端口):

方法 路径 说明 认证
GET /api/health 健康检查 无需
GET /api/books 获取图书列表(支持分页/关键词搜索) 无需
GET /api/books/:id 获取单本图书详情 无需
POST /api/user/login 用户登录,返回 Token 无需
POST /api/order/create 创建订单 Bearer Token
GET /api/order/list 查询订单列表 Bearer Token
bash 复制代码
# 启动服务
nohup python3 /opt/bookstore_api.py > /var/log/bookstore.log 2>&1 &

# 验证服务状态
root@ecs-916d-0001:~# curl -s http://localhost:8080/api/health
{"service":"悦购图书商城 API","status":"ok","version":"1.0.0"}

2.2 接口文档分析示例

以「用户登录」接口为例,拆解接口文档:

复制代码
接口名称:用户登录
请求方法:POST
请求路径:/api/user/login
请求头:Content-Type: application/json

请求参数(Body JSON):
  email    string  必填  用户邮箱,格式:xxx@xxx.xxx
  password string  必填  密码,6-20位

响应示例(成功):
  HTTP 200
  {"code":200,"message":"登录成功","data":{"token":"abc123token","name":"测试用户"}}

响应示例(失败):
  HTTP 400 - 参数缺失  {"code":400,"message":"邮箱不能为空"}
  HTTP 401 - 密码错误  {"code":401,"message":"用户名或密码错误"}

三、curl 原始接口测试

curl 是最基础的接口测试工具,掌握它能帮助快速验证接口可达性,也是理解 HTTP 请求结构的最佳方式。

3.1 GET 请求测试

bash 复制代码
# 健康检查 - 验证服务可用性
root@ecs-916d-0001:~# curl -s -w "\nHTTP_CODE:%{http_code} TIME:%{time_total}s" \
  http://localhost:8080/api/health

# 实际输出:
{"service":"悦购图书商城 API","status":"ok","version":"1.0.0"}
HTTP_CODE:200 TIME:0.003124s
bash 复制代码
# 获取图书列表(分页)
root@ecs-916d-0001:~# curl -s "http://localhost:8080/api/books?page=1&size=10"

# 实际输出:
{
    "code": 200,
    "data": [
        {"author": "Eric Matthes", "id": 1, "price": 89.0, "stock": 100, "title": "Python编程:从入门到实践"},
        {"author": "Cay S. Horstmann", "id": 2, "price": 149.0, "stock": 50, "title": "Java核心技术"},
        {"author": "Glenford J. Myers", "id": 3, "price": 59.0, "stock": 200, "title": "软件测试的艺术"},
        {"author": "David Gourley", "id": 4, "price": 119.0, "stock": 30, "title": "HTTP权威指南"}
    ],
    "page": 1,
    "size": 10,
    "total": 4
}
bash 复制代码
# 关键词搜索
root@ecs-916d-0001:~# curl -s "http://localhost:8080/api/books?keyword=Python"

# 实际输出:
{
    "code": 200,
    "data": [{"author": "Eric Matthes", "id": 1, "price": 89.0, "stock": 100, "title": "Python编程:从入门到实践"}],
    "page": 1,
    "size": 10,
    "total": 1
}
bash 复制代码
# 异常场景:访问不存在的图书(预期 404)
root@ecs-916d-0001:~# curl -s -w "\nHTTP_CODE:%{http_code}" http://localhost:8080/api/books/9999

# 实际输出:
{"code":404,"message":"图书不存在"}
HTTP_CODE:404

3.2 POST 请求测试

bash 复制代码
# 用户登录(正常场景)
root@ecs-916d-0001:~# curl -s -X POST http://localhost:8080/api/user/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"123456"}'

# 实际输出:
{
    "code": 200,
    "data": {
        "name": "测试用户",
        "token": "abc123token"
    },
    "message": "登录成功"
}
bash 复制代码
# 密码错误(异常场景,预期 401)
root@ecs-916d-0001:~# curl -s -w "\nHTTP_CODE:%{http_code}" \
  -X POST http://localhost:8080/api/user/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"wrong"}'

# 实际输出:
{"code":401,"message":"用户名或密码错误"}
HTTP_CODE:401
bash 复制代码
# 带 Token 认证:创建订单
root@ecs-916d-0001:~# curl -s -X POST http://localhost:8080/api/order/create \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer abc123token" \
  -d '{"book_id":1,"quantity":2}'

# 实际输出:
{
    "code": 200,
    "data": {
        "book_id": 1,
        "order_id": 1001,
        "quantity": 2,
        "title": "Python编程:从入门到实践",
        "total": 178.0
    },
    "message": "下单成功"
}
bash 复制代码
# 未携带 Token(预期 401)
root@ecs-916d-0001:~# curl -s -w "\nHTTP_CODE:%{http_code}" \
  -X POST http://localhost:8080/api/order/create \
  -H "Content-Type: application/json" \
  -d '{"book_id":1}'

# 实际输出:
{"code":401,"message":"未授权,请先登录"}
HTTP_CODE:401

四、使用 Postman/Newman 进行接口测试

4.1 Postman 简介与安装

Postman 是最流行的 API 测试工具,提供图形界面,支持请求管理、环境变量、断言脚本、Mock Server 等功能。

下载安装(Windows/Mac/Linux):

核心界面区域

复制代码
┌─────────────────────────────────────────────────────────┐
│  Collection 面板  │           请求编辑区                 │
│  ┌─────────────┐  │  ┌──────────────────────────────┐   │
│  │ 悦购API测试  │  │  │ GET ▼ http://xxx/api/books   │   │
│  │  ├ 健康检查  │  │  ├──────────────────────────────┤   │
│  │  ├ 图书列表  │  │  │ Params | Headers | Body | ... │   │
│  │  ├ 用户登录  │  │  ├──────────────────────────────┤   │
│  │  └ 创建订单  │  │  │ Tests (断言脚本)               │   │
│  └─────────────┘  │  └──────────────────────────────┘   │
└─────────────────────────────────────────────────────────┘

4.2 发送 GET 请求

在 Postman 中配置图书列表请求:

  • Method: GET
  • URL : http://121.36.51.148:8080/api/books
  • Params 标签页添加查询参数:
    • page = 1
    • size = 10

4.3 发送 POST 请求

用户登录请求配置:

  • Method: POST

  • URL : http://121.36.51.148:8080/api/user/login

  • Headers : Content-Type: application/json

  • Body → raw → JSON:

    json 复制代码
    {
      "email": "test@example.com",
      "password": "123456"
    }

4.4 Tests 中添加断言

在 Tests 标签页中用 JavaScript 编写断言:

javascript 复制代码
// 断言1:验证状态码为 200
pm.test("状态码为200", function () {
    pm.response.to.have.status(200);
});

// 断言2:验证响应体中 code 字段为 200
pm.test("业务code为200", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.code).to.eql(200);
});

// 断言3:验证返回的 token 存在
pm.test("Token不为空", function () {
    var jsonData = pm.response.json();
    pm.expect(jsonData.data.token).to.exist;
});

// 断言4:响应时间小于 500ms
pm.test("响应时间小于500ms", function () {
    pm.expect(pm.response.responseTime).to.be.below(500);
});

// 断言5:提取 token 存入环境变量(供后续接口使用)
pm.environment.set("token", pm.response.json().data.token);

4.5 环境变量与接口依赖

通过环境变量实现接口间的数据传递(登录 → 下单):

javascript 复制代码
// 在"用户登录"请求的 Tests 中提取 token
pm.environment.set("token", pm.response.json().data.token);

// 在"创建订单"请求的 Headers 中使用变量
// Authorization: Bearer {{token}}

环境变量设置步骤

  1. 右上角 → Environments → Add
  2. 新建环境 bookstore-dev
  3. 添加变量:base_url = http://121.36.51.148:8080

4.6 Collection 管理与导出

将所有接口组织到一个 Collection 中便于统一管理:

复制代码
悦购图书商城 API 接口测试集合
├── 第一节:基础查询
│   ├── GET 健康检查
│   ├── GET 图书列表(分页)
│   ├── GET 关键词搜索
│   └── GET 图书详情
├── 第二节:用户认证
│   ├── POST 登录(正常)
│   ├── POST 登录(密码错误)
│   └── POST 登录(邮箱为空)
└── 第三节:订单业务
    ├── POST 创建订单(已认证)
    ├── POST 创建订单(未认证)
    └── GET 查询订单列表

导出 Collection:Collection → ... → Export → Collection v2.1

4.7 Newman 命令行执行(itest-02: 119.3.175.121)

Newman 是 Postman 的命令行运行器,实现无界面的 CI 集成。

安装 Newman(ecs-916d-0002 节点):

bash 复制代码
# 安装 Node.js 和 npm
root@ecs-916d-0002:~# apt-get install -y nodejs npm openjdk-17-jdk

# 安装 Newman 及 HTML 报告插件
root@ecs-916d-0002:~# npm install -g newman newman-reporter-htmlextra

# 验证安装
root@ecs-916d-0002:~# newman --version
6.2.2

执行 Collection

bash 复制代码
root@ecs-916d-0002:~# newman run /opt/postman-tests/bookstore_collection.json \
  --reporters cli

真实执行输出

复制代码
newman

悦购图书商城 API 接口测试集合

→ 健康检查
  GET http://121.36.51.148:8080/api/health [200 OK, 252B, 28ms]
  ✓  状态码为200
  ✓  服务状态正常

→ 获取图书列表
  GET 121.36.51.148:8080/api/books?page=1&size=10 [200 OK, 661B, 10ms]
  ✓  状态码为200
  ✓  返回图书列表
  ✓  响应时间小于500ms

→ 关键词搜索图书
  GET 121.36.51.148:8080/api/books?keyword=Python [200 OK, 346B, 11ms]
  ✓  搜索结果包含Python图书

→ 获取单本图书详情
  GET http://121.36.51.148:8080/api/books/1 [200 OK, 315B, 7ms]
  ✓  返回图书详情
  ✓  图书包含必要字段

→ 获取不存在的图书(异常场景)
  GET http://121.36.51.148:8080/api/books/9999 [404 NOT FOUND, 228B, 8ms]
  ✓  返回404状态码
  ✓  返回错误信息

→ 用户登录(正常场景)
  POST http://121.36.51.148:8080/api/user/login [200 OK, 281B, 9ms]
  ✓  登录成功

→ 用户登录(密码错误)
  POST http://121.36.51.148:8080/api/user/login [401 UNAUTHORIZED, 249B, 11ms]
  ✓  密码错误返回401

→ 用户登录(邮箱为空)
  POST http://121.36.51.148:8080/api/user/login [400 BAD REQUEST, 236B, 8ms]
  ✓  邮箱为空返回400

→ 创建订单(已登录)
  POST http://121.36.51.148:8080/api/order/create [200 OK, 351B, 7ms]
  ✓  创建订单成功
  ✓  订单金额正确

→ 创建订单(未授权)
  POST http://121.36.51.148:8080/api/order/create [401 UNAUTHORIZED, 249B, 7ms]
  ✓  未授权返回401

┌─────────────────────────┬──────────────────┬─────────────────┐
│                         │         executed │          failed │
├─────────────────────────┼──────────────────┼─────────────────┤
│              iterations │                1 │               0 │
├─────────────────────────┼──────────────────┼─────────────────┤
│                requests │               10 │               0 │
├─────────────────────────┼──────────────────┼─────────────────┤
│            test-scripts │               10 │               0 │
├─────────────────────────┼──────────────────┼─────────────────┤
│      prerequest-scripts │                0 │               0 │
├─────────────────────────┼──────────────────┼─────────────────┤
│              assertions │               16 │               0 │
├─────────────────────────┴──────────────────┴─────────────────┤
│ total run duration: 251ms                                    │
├──────────────────────────────────────────────────────────────┤
│ total data received: 1.48kB (approx)                         │
├──────────────────────────────────────────────────────────────┤
│ average response time: 10ms [min: 7ms, max: 28ms, s.d.: 5ms] │
└──────────────────────────────────────────────────────────────┘

结果 :10 个请求,16 个断言,全部通过(0 失败),平均响应时间 10ms。

生成 HTML 报告

bash 复制代码
root@ecs-916d-0002:~# newman run /opt/postman-tests/bookstore_collection.json \
  --reporters cli,htmlextra \
  --reporter-htmlextra-export /opt/postman-tests/report.html

五、使用 JMeter 进行接口测试

5.1 JMeter 简介

Apache JMeter 是一款纯 Java 的开源负载测试工具,既可做接口功能测试,也可做性能压力测试。相比 Postman,JMeter 更适合:

  • 需要多线程并发模拟的场景

  • 需要命令行无 GUI 执行的 CI/CD 集成

  • 需要生成专业 HTML 报告

  • 测试规模较大(数百个接口、数千并发)

    JMeter 工程结构(元件树):
    测试计划 (Test Plan)
    └── 线程组 (Thread Group) ← 并发用户数配置
    ├── HTTP 请求默认值 (Config) ← 公共配置(域名、端口)
    ├── HTTP 信息头管理器 (Header Manager) ← 公共 Headers
    ├── 取样器 (Sampler) ← 具体的接口请求
    │ ├── 断言 (Assertion) ← 验证响应
    │ ├── 提取器 (Extractor) ← 提取变量
    │ └── 监听器 (Listener) ← 查看结果
    └── ...

5.2 JMeter 安装(itest-03: 1.92.93.183)

bash 复制代码
# 安装 JDK 17(JMeter 5.6+ 要求 Java 8+)
root@ecs-916d-0003:~# apt-get install -y openjdk-17-jdk
root@ecs-916d-0003:~# java -version
openjdk version "17.0.19" 2026-04-21
OpenJDK Runtime Environment (build 17.0.19+10-1-24.04.2-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.19+10-1-24.04.2-Ubuntu, mixed mode, sharing)

# 下载 JMeter(阿里云镜像加速)
root@ecs-916d-0003:~# wget -q \
  https://mirrors.aliyun.com/apache/jmeter/binaries/apache-jmeter-5.6.3.tgz \
  -O /tmp/jmeter.tgz

# 解压并配置
root@ecs-916d-0003:~# tar -xzf /tmp/jmeter.tgz -C /opt/
root@ecs-916d-0003:~# ln -sf /opt/apache-jmeter-5.6.3 /opt/jmeter
root@ecs-916d-0003:~# echo 'export PATH=$PATH:/opt/jmeter/bin' >> /etc/profile
root@ecs-916d-0003:~# source /etc/profile

# 验证安装
root@ecs-916d-0003:~# jmeter --version 2>&1 | grep -i "5\."
    /_/   \_\_| /_/   \_\____|_| |_|_____|  \___/|_|  |_|_____| |_| |_____|_| \_\ 5.6.3

5.3 核心组件详解

线程组(Thread Group)

线程组控制并发用户数量,是 JMeter 测试的基础:

复制代码
线程组参数:
  线程数(Threads)      = 5      ← 模拟 5 个并发用户
  Ramp-Up 时间(秒)     = 2      ← 2 秒内依次启动全部线程
  循环次数(Loop Count) = 3      ← 每个线程执行 3 次
  总请求数              = 5×3×4  = 60 个请求(4个取样器)
取样器(HTTP Request Sampler)
xml 复制代码
<!-- GET 请求示例 -->
<HTTPSamplerProxy testname="GET 图书列表">
  <stringProp name="HTTPSampler.path">/api/books</stringProp>
  <stringProp name="HTTPSampler.method">GET</stringProp>
  <!-- 查询参数 -->
  <elementProp name="HTTPsampler.Arguments">
    <elementProp name="page"><stringProp name="Argument.value">1</stringProp></elementProp>
    <elementProp name="size"><stringProp name="Argument.value">10</stringProp></elementProp>
  </elementProp>
</HTTPSamplerProxy>

<!-- POST 请求示例(Raw Body) -->
<HTTPSamplerProxy testname="POST 用户登录">
  <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
  <stringProp name="Argument.value">{"email":"test@example.com","password":"123456"}</stringProp>
  <stringProp name="HTTPSampler.path">/api/user/login</stringProp>
  <stringProp name="HTTPSampler.method">POST</stringProp>
</HTTPSamplerProxy>
响应断言
xml 复制代码
<!-- 断言 HTTP 状态码 -->
<ResponseAssertion testname="断言-状态码200">
  <stringProp>200</stringProp>
  <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
  <intProp name="Assertion.test_type">8</intProp>  <!-- 8=equals -->
</ResponseAssertion>
JSON 提取器(接口依赖)
xml 复制代码
<!-- 从登录响应中提取 token,供后续请求使用 -->
<JSONPostProcessor testname="JSON提取器-Token">
  <stringProp name="JSONPostProcessor.referenceNames">login_token</stringProp>
  <stringProp name="JSONPostProcessor.jsonPathExprs">$.data.token</stringProp>
</JSONPostProcessor>

<!-- 在创建订单请求的 Header Manager 中引用变量 -->
<!-- Authorization: Bearer ${login_token} -->
JSON 断言
xml 复制代码
<!-- 验证响应体中 $.code == 200 -->
<JSONPathAssertion testname="JSON断言-code">
  <stringProp name="JSON_PATH">$.code</stringProp>
  <stringProp name="EXPECTED_VALUE">200</stringProp>
  <boolProp name="JSONVALIDATION">true</boolProp>
</JSONPathAssertion>

5.4 JMX 脚本结构(完整示例)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" jmeter="5.6.3">
  <hashTree>
    <TestPlan testname="悦购图书商城接口测试">
      <!-- 全局用户自定义变量 -->
      <elementProp name="TestPlan.user_defined_variables">
        <elementProp name="BASE_URL"><stringProp name="Argument.value">121.36.51.148</stringProp></elementProp>
        <elementProp name="PORT"><stringProp name="Argument.value">8080</stringProp></elementProp>
        <elementProp name="TOKEN"><stringProp name="Argument.value">abc123token</stringProp></elementProp>
      </elementProp>
    </TestPlan>
    <hashTree>
      <!-- 线程组:5线程 × 3次循环 -->
      <ThreadGroup testname="接口测试线程组">
        <stringProp name="ThreadGroup.num_threads">5</stringProp>
        <stringProp name="ThreadGroup.ramp_time">2</stringProp>
        <stringProp name="LoopController.loops">3</stringProp>
      </ThreadGroup>
      <hashTree>
        <!-- HTTP 请求默认值(域名/端口复用) -->
        <ConfigTestElement testname="HTTP请求默认值">
          <stringProp name="HTTPSampler.domain">${BASE_URL}</stringProp>
          <stringProp name="HTTPSampler.port">${PORT}</stringProp>
          <stringProp name="HTTPSampler.protocol">http</stringProp>
        </ConfigTestElement>

        <!-- 取样器1:GET 健康检查(含响应断言) -->
        <!-- 取样器2:GET 图书列表(含 JSON 断言) -->
        <!-- 取样器3:POST 用户登录(含 JSON 提取器) -->
        <!-- 取样器4:POST 创建订单(含 Authorization Header) -->

        <!-- 结果收集器 -->
        <ResultCollector testname="汇总报告">
          <stringProp name="filename">/opt/jmeter-results/bookstore_result.jtl</stringProp>
        </ResultCollector>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

5.5 命令行执行与结果分析

命令行参数说明

bash 复制代码
/opt/jmeter/bin/jmeter \
  -n                 # non-GUI 模式(无界面,适合服务器/CI)
  -t <test.jmx>     # 指定测试脚本
  -l <result.jtl>   # 指定结果输出文件(JTL 格式)
  -e                # 执行完成后生成 HTML 报告
  -o <report-dir>   # HTML 报告输出目录(需为空目录)

实际执行过程

bash 复制代码
root@ecs-916d-0003:~# mkdir -p /opt/jmeter-tests /opt/jmeter-results

root@ecs-916d-0003:~# /opt/jmeter/bin/jmeter \
  -n -t /opt/jmeter-tests/bookstore_test.jmx \
  -l /opt/jmeter-results/bookstore_result.jtl \
  -e -o /opt/jmeter-results/html-report 2>&1 | grep -v WARN

Creating summariser <summary>
Created the tree successfully using /opt/jmeter-tests/bookstore_test.jmx
Starting standalone test @ 2026 Jun 30 17:43:14 CST (1782812594041)
Waiting for possible Shutdown/StopTestNow/HeapDump/ThreadDump message on port 4445
summary =     60 in 00:00:02 =   34.5/s Avg:     8 Min:     6 Max:    25 Err:     0 (0.00%)
Tidying up ...    @ 2026 Jun 30 17:43:15 CST (1782812595845)
... end of run

结果解读

复制代码
summary =  60 in 00:00:02 = 34.5/s  Avg: 8  Min: 6  Max: 25  Err: 0 (0.00%)
           │       │         │        │       │       │         └── 错误率:0%  ✅
           │       │         │        │       │       └── 最大响应时间:25ms
           │       │         │        │       └── 最小响应时间:6ms
           │       │         │        └── 平均响应时间:8ms
           │       │         └── 吞吐量:34.5 请求/秒
           │       └── 执行耗时:2秒
           └── 总请求数:60(5线程 × 3次循环 × 4个接口)

各接口性能汇总(从 JTL 文件统计):

复制代码
接口名称             样本数  通过数  平均(ms)  最小(ms)  最大(ms)
GET 健康检查           15     15      9.9        7        25
GET 图书列表           15     15      8.5        6        11
POST 用户登录          15     15      8.1        6        10
POST 创建订单          15     15      8.9        6        11

全部 60 个请求通过,错误率 0.00%,所有接口平均响应时间均在 10ms 以内(内网环境)。

5.6 常见 JMeter 组件用法

CSV 数据文件参数化
bash 复制代码
# 创建测试数据文件
cat > /opt/jmeter-tests/books.csv << EOF
book_id,quantity
1,1
2,2
3,3
4,1
EOF

在 JMX 中配置 CSV Data Set Config:

xml 复制代码
<CSVDataSet testname="CSV数据文件配置">
  <stringProp name="filename">/opt/jmeter-tests/books.csv</stringProp>
  <stringProp name="variableNames">book_id,quantity</stringProp>
  <stringProp name="delimiter">,</stringProp>
  <boolProp name="recycle">true</boolProp>
</CSVDataSet>
<!-- 请求中使用 ${book_id} 和 ${quantity} 变量 -->
逻辑控制器
复制代码
循环控制器(Loop Controller):重复执行子节点 N 次
If 控制器(If Controller):条件判断(如 ${responseCode} == 200)
ForEach 控制器:遍历变量集合
事务控制器(Transaction Controller):将多个请求组合为一个事务统计
函数助手

常用内置函数(在 JMeter 菜单 Tools → Function Helper Dialog 中调试):

复制代码
${__time(yyyy-MM-dd HH:mm:ss)}     # 当前时间
${__Random(1,100)}                  # 1~100 随机数
${__UUID()}                         # 生成 UUID
${__MD5(${password})}               # MD5 加密
${__CSVRead(file.csv,0)}            # 读取 CSV 第0列

六、Fiddler 抓包分析

6.1 Fiddler 工作原理

Fiddler 作为本地 HTTP 代理(默认端口 8888),拦截并记录所有流经的 HTTP/HTTPS 流量:

复制代码
             ┌──────────────┐
  浏览器/App ──▶  Fiddler   ──▶  目标服务器
             │  (localhost  │
             │   :8888)     │
             └──────────────┘
                 ↕ 记录
              会话列表面板

6.2 HTTPS 抓包配置

复制代码
1. 菜单 Tools → Options → HTTPS
2. 勾选 "Decrypt HTTPS traffic"
3. 点击 "Actions" → Trust Root Certificate
4. 在浏览器或系统证书管理中导入 Fiddler 根证书

踩坑记录:Android 7.0+ 默认不信任用户证书,需要 root 设备或使用 Android 模拟器。

6.3 手机端抓包

复制代码
1. 确保手机与电脑在同一局域网
2. Fiddler: Tools → Options → Connections → 勾选 "Allow remote computers to connect"
3. 手机 WiFi 高级设置 → 手动代理 → 填入电脑 IP:8888
4. 手机浏览器访问 http://电脑IP:8888 → 下载安装证书

6.4 Fiddler 主要功能

功能 操作 用途
断点修改请求 Rules → Automatic Breakpoints → Before Requests 修改请求参数后再发出
断点修改响应 Rules → Automatic Breakpoints → After Responses 模拟不同响应场景
Mock 功能 AutoResponder 标签页 伪造接口响应,脱离后端测试前端
数据过滤 Filters 标签页 只显示特定域名/状态码的请求
弱网测试 Rules → Customize Rules → m_SimulateModem 模拟 2G/3G 网络延迟
FiddlerScript Rules → Customize Rules 自定义 C# 脚本批量处理请求

七、接口测试用例设计实战

7.1 用例模板

字段 说明
用例编号 TC_模块_序号,如 TC_LOGIN_001
用例名称 简洁描述场景,如「登录成功-正常账号密码」
接口路径 POST /api/user/login
请求方法 POST
请求头 Content-Type: application/json
请求体 {"email":"test@example.com","password":"123456"}
前置条件 用户已注册
预期结果 HTTP 200,code=200,token 不为空
实际结果 (执行后填写)
是否通过 ✅/❌

7.2 悦购图书商城接口测试用例设计(完整表)

用户登录模块(TC_LOGIN)

编号 场景 请求 Body 预期状态码 预期结果
TC_LOGIN_001 正常登录 {"email":"test@example.com","password":"123456"} 200 code=200,token 非空
TC_LOGIN_002 密码错误 {"email":"test@example.com","password":"wrong"} 401 message="用户名或密码错误"
TC_LOGIN_003 邮箱为空 {"email":"","password":"123456"} 400 message="邮箱不能为空"
TC_LOGIN_004 密码为空 {"email":"test@example.com","password":""} 400 message="密码不能为空"
TC_LOGIN_005 Body 为空 {} 400 code=400
TC_LOGIN_006 邮箱格式错误 {"email":"notanemail","password":"123456"} 400/401 返回错误提示

图书查询模块(TC_BOOK)

编号 场景 请求参数 预期状态码 预期结果
TC_BOOK_001 获取全部图书 page=1&size=10 200 data 数组非空
TC_BOOK_002 关键词搜索 keyword=Python 200 total≥1,结果包含 Python
TC_BOOK_003 无结果搜索 keyword=不存在的书 200 total=0,data=\[\]
TC_BOOK_004 获取图书详情 /api/books/1 200 id=1,包含所有字段
TC_BOOK_005 图书 ID 不存在 /api/books/9999 404 code=404
TC_BOOK_006 分页第2页 page=2&size=2 200 data 最多2条

订单模块(TC_ORDER)

编号 场景 Authorization 请求 Body 预期结果
TC_ORDER_001 创建订单(已登录) Bearer abc123token {"book_id":1,"quantity":2} 200,订单创建成功
TC_ORDER_002 创建订单(未登录) {"book_id":1,"quantity":1} 401,未授权
TC_ORDER_003 图书ID不存在 Bearer abc123token {"book_id":9999} 404
TC_ORDER_004 库存不足 Bearer abc123token {"book_id":4,"quantity":999} 400,库存不足
TC_ORDER_005 查询订单列表 Bearer abc123token - 200,返回订单列表

八、Newman + Ant 集成持续测试

8.1 Ant 安装(itest-04: 120.46.82.41)

bash 复制代码
root@ecs-916d-0004:~# apt-get install -y openjdk-17-jdk ant
root@ecs-916d-0004:~# ant -version
Apache Ant(TM) version 1.10.14 compiled on September 25 2023

8.2 Newman 生成 JUnit 报告集成 Ant

bash 复制代码
# 生成 JUnit XML 格式报告(可被 Jenkins/Ant 解析)
root@ecs-916d-0002:~# newman run /opt/postman-tests/bookstore_collection.json \
  --reporters cli,junit \
  --reporter-junit-export /opt/postman-tests/junit-report.xml

build.xml 示例(Ant 集成 Newman):

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project name="bookstore-api-test" default="run-newman-test" basedir=".">

  <property name="collection.file" value="/opt/postman-tests/bookstore_collection.json"/>
  <property name="report.dir"      value="/opt/postman-tests/reports"/>

  <target name="clean">
    <delete dir="${report.dir}"/>
    <mkdir dir="${report.dir}"/>
  </target>

  <target name="run-newman-test" depends="clean">
    <exec executable="newman" failonerror="true">
      <arg value="run"/>
      <arg value="${collection.file}"/>
      <arg value="--reporters"/>
      <arg value="cli,junit,htmlextra"/>
      <arg value="--reporter-junit-export"/>
      <arg value="${report.dir}/junit-report.xml"/>
      <arg value="--reporter-htmlextra-export"/>
      <arg value="${report.dir}/html-report.html"/>
    </exec>
  </target>

  <target name="check-result" depends="run-newman-test">
    <junitreport todir="${report.dir}">
      <fileset dir="${report.dir}">
        <include name="junit-report.xml"/>
      </fileset>
      <report format="frames" todir="${report.dir}/html"/>
    </junitreport>
    <echo message="测试报告已生成:${report.dir}/html/index.html"/>
  </target>

</project>
bash 复制代码
# 执行 Ant 构建
root@ecs-916d-0004:~# ant run-newman-test

8.3 JMeter 命令行 + 报告生成

bash 复制代码
# 完整命令:执行测试 + 生成 HTML 报告
root@ecs-916d-0003:~# /opt/jmeter/bin/jmeter \
  -n \
  -t /opt/jmeter-tests/bookstore_test.jmx \
  -l /opt/jmeter-results/result_$(date +%Y%m%d_%H%M%S).jtl \
  -e -o /opt/jmeter-results/html-report \
  2>&1 | grep -v WARN

# 输出示例:
Creating summariser <summary>
Created the tree successfully using bookstore_test.jmx
Starting standalone test @ 2026 Jun 30 17:43:14 CST
summary =  60 in 00:00:02 = 34.5/s Avg: 8 Min: 6 Max: 25 Err: 0 (0.00%)
... end of run

# 查看 HTML 报告(将报告目录映射到 Web 服务器访问)
root@ecs-916d-0003:~# python3 -m http.server 8090 --directory /opt/jmeter-results/
# 浏览器访问 http://1.92.93.183:8090/html-report/

九、踩坑记录

9.1 JMeter Authorization Header 不生效

现象 :POST 请求需要携带 Authorization Header,但 401 响应一直出现。

原因 :在 JMX 的 HTTPSamplerProxy 标签内嵌套 elementProp name="HTTPSampler.header" 这种写法在 JMeter 5.x 中不被识别。

正确做法 :单独在 hashTree 子节点中添加 HeaderManager 元件:

xml 复制代码
<HTTPSamplerProxy testname="POST 创建订单">
  ...
</HTTPSamplerProxy>
<hashTree>
  <!-- 正确:独立的 HeaderManager 子元件 -->
  <HeaderManager testname="创建订单Header">
    <collectionProp name="HeaderManager.headers">
      <elementProp name="" elementType="Header">
        <stringProp name="Header.name">Authorization</stringProp>
        <stringProp name="Header.value">Bearer ${TOKEN}</stringProp>
      </elementProp>
    </collectionProp>
  </HeaderManager>
  <hashTree/>
</hashTree>

9.2 Flask pip3 安装报错

现象pip3 install flaskCannot uninstall blinker, RECORD file not found

原因:Ubuntu 24.04 的 apt 安装的部分 Python 包与 pip 管理的包冲突。

解决

bash 复制代码
pip3 install 'flask>=3.0' --break-system-packages --ignore-installed

9.3 Newman 版本与 Collection 格式

Newman 6.x 要求 Collection 格式为 v2.1(schema.getpostman.com/json/collection/v2.1.0),如果是从老版本 Postman 导出的 v1 格式,运行会报错。

解决 :在 Postman 中重新导出为 v2.1 格式,或手动修改 schema 字段。

9.4 JMeter CSV 参数化中文乱码

现象:CSV 中含有中文,JMeter 读取后在请求中出现乱码。

解决

  1. CSV 文件保存为 UTF-8(无 BOM)
  2. CSVDataSet 中设置 fileEncoding=UTF-8
  3. HTTP 请求默认值中设置 contentEncoding=UTF-8

十、总结

本文通过悦购图书商城 REST API 作为被测系统,完整演示了接口自动化测试的全链路:

复制代码
接口分析(文档阅读) → curl 验证 → Postman 用例编写
       ↓
Newman 命令行执行 → HTML/JUnit 报告生成
       ↓
JMeter JMX 脚本编写 → 命令行并发执行 → HTML 报告分析
       ↓
Ant 构建集成 → 持续测试

工具选型建议

场景 推荐工具
快速验证单个接口 curl
接口用例管理 + 手动测试 Postman
CI/CD 中自动化执行 Newman + Ant
高并发性能 + 大规模接口 JMeter
HTTPS 抓包 + 问题定位 Fiddler

测试结果汇总

工具 用例数 通过 失败 平均响应
Newman 10 请求 / 16 断言 16 0 10ms
JMeter 60 个请求 (5线程×3循环×4接口) 60 0 8ms

接口测试是软件质量保障的重要一环,掌握 Postman、Newman、JMeter 三件套,能够覆盖从开发联调、回归测试到性能验证的全生命周期。


环境:华为云 ecs-916d 集群 | Ubuntu 24.04 | Python 3.12.3 / Flask 3.1.3 | JMeter 5.6.3 | Newman 6.2.2 | OpenJDK 17.0.19