手动API测试及API测试工具的使用

HTTP(超文本传输协议)详解

为什么说HTTP是超文本传输协议?

这个名称精确地概括了HTTP的核心本质,我们可以拆解来看:

  1. 超文本
    • “文本” 指的是文字、字符等基本信息。
    • “超”意味着“超越”或“链接”。超文本是一种包含超链接的文本,允许用户从一个文档跳转到另一个相关文档。
    • 最典型的超文本系统就是万维网(WWW)。我们浏览网页时,通过点击链接从一个页面跳转到另一个页面,这正是“超文本”的体现。
    • 如今,HTTP传输的早已不限于文本,还包括图片、视频、音频、CSS、JavaScript等所有Web资源,但它的设计初衷和核心思想源于“超文本”。
  2. 传输
    • 这指明了HTTP协议的主要作用——在两点之间搬运数据
    • 通常,这两点是客户端服务器端。客户端(Web浏览器)发出请求,服务器端(后台应用服务器)返回响应。
  3. 协议
    • 协议是一套预先定义好的规则和约定。HTTP规定了客户端和服务器之间通信的语法、格式和顺序。双方都必须遵守这套规则,才能正确理解对方的意图,完成数据的交换。

HTTP协议简介

HTTP 是一种用于分布式、协作式和超媒体信息系统的应用层协议。它是万维网(WWW)数据通信的基础。

  • 客户端-服务器模型:HTTP 遵循经典的客户端-服务器模型。浏览器(客户端)向服务器发送请求,服务器返回一个响应
  • 无状态协议:默认情况下,每个 HTTP 请求都是独立的,服务器不会保留之前请求的任何信息。为了实现状态(如用户登录),需要使用 Cookie、Session 等技术。
  • 基于 TCP/IP:HTTP 依赖于 TCP(或 TLS 加密的 TCP,即 HTTPS)来保证数据的可靠传输。

一个完整的 HTTP 事务包含四个步骤:

  1. 建立 TCP 连接。
  2. 客户端发送 HTTP 请求。
  3. 服务器处理请求并返回 HTTP 响应。
  4. 关闭 TCP 连接(在 HTTP/1.0 或 Connection: close 时;HTTP/1.1 默认使用持久连接)。

下面我们深入到请求和响应的各个部分。

1. HTTP请求方法

HTTP 方法指明了客户端希望服务器对资源执行的操作。它们也被称为“动词”。

方法 英文含义 描述 特点 幂等性 安全性 典型应用场景
GET Retrieve 请求指定的资源。只应用于获取数据,不应产生“副作用”(如修改数据)。 请求参数以查询字符串的形式附加在 URL 之后(如 ?key=value),有长度限制,且会在浏览器历史记录和日志中明文显示,不安全。 点击链接、在浏览器地址栏输入网址、搜索。
POST Create 提交指定的资源。只应用于提交数据,会导致服务器状态变化(如创建新资源、提交表单)。 请求参数放在 请求体(Body) 中,没有长度限制,相对 GET 更安全(不会在 URL 中显示)。 用户登录、发表评论、提交订单。
PUT Update/Replace 用请求体中的内容 完整替换 目标资源。如果资源不存在,则创建它。 常用于更新整个资源。 更新用户个人资料(全部字段)。
PATCH Update/Modify 用于对资源进行部分修改。与 PUT 替换整个资源不同,PATCH 只发送需要更新的字段。 在 RESTful API 中广泛应用。 ❓(取决于实现方式) 只修改用户的手机号。
DELETE Delete 请求服务器,删除 指定资源。 --- 删除一篇文章、注销账号。
HEAD Head 与 GET 类似,但服务器只返回响应头,不返回响应体。 用于获取资源的元信息,如检查资源是否存在、查看文件大小或最后修改时间,而无需下载整个内容。 检查链接是否有效、判断资源是否被修改过。
OPTIONS Options 用于获取目标资源所支持的通信选项(如支持的 HTTP 方法)。 CORS(跨域资源共享)预检请求。 A请求B中的某个属性
  • 幂等性: 相同的请求执行一次与执行多次,对资源的状态产生的影响是相同的。GET, PUT, DELETE都是幂等的,POST不是。
  • 安全性: 该方法是否仅用于获取数据,而不修改服务器上的资源。GET和HEAD是安全的。

最常用的是GET和POST。


2. HTTP状态码

状态码是一个三位数字,用于表示服务器对请求的处理结果。它分为5类:

概述 类别 含义 常见状态码
表示请求已被接收,需要继续处理。 1xx 信息性 100 Continue(继续)
101 Switching Protocols (切换协议)
表示请求已被服务器成功接收、理解并接受。 2xx 成功 200 OK(请求成功)
201 Created(已创建,常用于POST/PUT成功)
204 No Content(成功,但无内容返回)
表示需要客户端采取进一步的操作才能完成请求。 3xx 重定向 301 Moved Permanently(永久重定向)
302 Found(临时重定向)
304 Not Modified(资源未修改,使用缓存)
表示客户端似乎有错误,服务器无法处理请求。 4xx 客户端错误 400 Bad Request(请求语法错误)
401 Unauthorized(未认证)
403 Forbidden(服务器理解请求但拒绝执行)
404 Not Found(资源未找到)
405 Method Not Allowed(方法不被允许)
表示服务器在处理请求的过程中发生了错误。 5xx 服务器错误 500 Internal Server Error(服务器内部错误)
502 Bad Gateway(网关错误)
503 Service Unavailable(服务不可用)
504 Gateway Timeout(网关超时)

3. HTTP头部

HTTP 头部允许客户端和服务器通过请求和响应传递附加信息。它们是键值对,不区分大小写。

  • 通用头部
    可用于请求和响应消息。

    • Cache-Control:指定缓存机制。
    • Connection:控制本次事务完成后是否关闭网络连接。
    • Date:消息创建的日期和时间。
  • 请求头部
    提供关于请求、客户端或客户端的首选格式的更多信息。

    • Host:请求的目标主机和端口号(HTTP/1.1 必须)。
    • User-Agent:包含发起请求的客户端应用程序的信息。
    • Accept:告知服务器客户端能够处理的内容类型(如 Accept: text/html, application/json)。
    • Accept-Encoding:客户端支持的压缩格式(如 gzip, deflate)。
    • Authorization:包含用于服务器验证客户端身份的凭证(如 Bearer <token>)。
    • Cookie:将之前服务器通过 Set-Cookie 发送的 Cookie 回传给服务器。
    • Content-Type:请求体的媒体类型(如 application/json)。
    • Content-Length:请求体的字节长度。
  • 响应头部
    提供关于响应的额外信息。

    • Server:包含处理请求的服务器软件信息。
    • Set-Cookie:服务器向客户端发送 Cookie。
    • Content-Type:响应体的媒体类型(如 text/html; charset=utf-8)。
    • Content-Length:响应体的字节长度。
    • Content-Encoding:响应体使用的编码格式(如 gzip)。
    • Location:在重定向时使用,指定要重定向到的 URL。

4. HTTP 消息体

消息体是 HTTP 传输的核心数据部分,在请求和响应中都是可选的。
  • 请求体

    • 何时存在:通常在 POST、PUT、PATCH 等需要向服务器发送数据的请求中使用。
    • 格式:由 Content-Type 头部指定。
      • application/x-www-form-urlencoded:默认的表单提交格式,如 key1=value1&key2=value2
      • multipart/form-data:用于上传文件。
      • application/json:传输 JSON 格式的数据(RESTful API 常用)。
      • application/xml:传输 XML 格式的数据。
  • 响应体

    • 何时存在:服务器返回给客户端的实际内容,如 HTML 页面、JSON 数据、图片、CSS、JavaScript 等。
    • 格式:由响应头中的 Content-Type 指定。
      • text/html:HTML 文档。
      • application/json:JSON 数据。
      • image/png:PNG 图片。
      • application/javascript: JavaScript文件。

5. HTTP请求/响应示例

  • 请求:

    POST /api/login HTTP/1.1
    Host: www.example.com
    User-Agent: Mozilla/5.0...
    Content-Type: application/json
    Content-Length: 42
    
    {"username": "alice", "password": "secret"}
    
    • 第一行(请求行): POST(方法) /api/login(路径) HTTP/1.1(协议版本)。
    • 第2-5行(请求头): 包含了请求的元信息。
    • 空行: 分隔头部和消息体。
    • 最后一行(消息体): 实际发送的JSON数据。
  • 响应:

    HTTP/1.1 200 OK
    Content-Type: application/json
    Set-Cookie: sessionId=abc123; Path=/
    Content-Length: 29
    
    {"status": "success", "user": "Alice"}
    
    • 第一行(状态行): HTTP/1.1(协议版本) 200(状态码) OK(状态消息)。
    • 第2-4行(响应头): 包含了响应的元信息,包括设置一个Cookie。
    • 空行: 分隔头部和消息体。
    • 最后一行(消息体): 服务器返回的JSON数据。

6. 补充:HTTP的发展

  • HTTP/1.0: 每个请求/响应都需要建立一个新的TCP连接,效率低下。
  • HTTP/1.1(目前最主流):
    • 引入了持久连接,一个TCP连接可以处理多个请求。
    • 引入了管道化,但存在队头阻塞问题。
  • HTTP/2:
    • 二进制协议,性能更高。
    • 多路复用,解决了队头阻塞。
    • 服务器推送。
  • HTTP/3:
    • 基于QUIC协议(运行在UDP上),进一步减少连接建立延迟。
    • 解决了TCP层面的队头阻塞问题。

总结

组成部分 作用 关键示例
方法 定义操作类型 GET(取),POST(增),PUT(改),DELETE(删)
状态码 表示请求结果 200(成功),404(未找到),500(服务器错误)
头部 传递元信息 Content-Type(数据类型),Authorization(认证)
消息体 传输实际数据 HTML代码,JSON字符串,表单数据

理解这四个核心部分,是掌握 Web 开发、API 设计和网络调试的基础。它们共同协作,完成了我们在互联网上的每一次点击和浏览。


RESTful API 设计规范

核心思想:为什么测试需要关心设计?

作为测试工程师,我们关心的不仅仅是 API “能不能跑通”,更是它的可测试性、可维护性、稳定性和一致性。一个符合规范的 RESTful API 天然具备这些特性,反之,一个设计混乱的 API 会让测试工作变得异常困难和低效。


RESTful API 设计规范详解与测试视角

RESTful API 的核心是 Representational State Transfer,它是一套架构约束,而非硬性标准。其核心规范可以归纳为以下几个方面:

1. 面向资源与正确的 URI 设计

规范理解:
API 应该围绕“资源”来设计,URI 应该只用于标识资源,而不应包含任何操作(动词)。资源通常是名词,并且最好是复数形式。

  • 测试意义:
    • 清晰度高: URI 结构一致,测试人员可以轻松推断出 API 的功能。例如,看到 /products 就知道是操作产品集合。
    • 用例设计简单: 针对同一资源的增删改查(CRUD)用例可以基于统一的 URI 模板来设计。

举例说明:

  • 良好设计:

    • GET /users - 获取用户列表
    • POST /users - 创建新用户
    • GET /users/123 - 获取 ID 为 123 的用户详情
    • PUT /users/123 - 整体更新 ID 为 123 的用户
    • PATCH /users/123 - 部分更新 ID 为 123 的用户
    • DELETE /users/123 - 删除 ID 为 123 的用户
  • 不良设计(测试痛点):

    • GET /getAllUsers - (使用了动词,且无法通过 URI 推断资源)
    • POST /createUser - (动作在 URI 中,与 POST 方法语义重复)
    • GET /getUserById?id=123 - (操作感太强,不符合 REST 风格)
    • POST /updateUser - (用 POST 模拟更新操作,方法使用错误)

测试关注点:

  • 检查 URI 是否全部使用名词(复数)。
  • 检查对于层级资源,URI 是否清晰,例如 GET /users/123/orders(获取用户 123 的所有订单)。

2. 正确使用 HTTP 方法

规范理解:
HTTP 方法(动词)应该用于定义对资源的操作,其语义必须与 HTTP 标准保持一致。

  • 测试意义:
    • 幂等性: GET, PUT, DELETE 是幂等的,即多次重复请求与一次请求的效果相同。这对于测试重试机制、网络超时等场景至关重要。
    • 安全性: GET 是安全的,不应改变资源状态。测试时可以放心调用而不用担心副作用。
    • 用例覆盖: 需要针对不同方法设计不同的测试场景。

HTTP 方法语义表:

方法 描述 测试关注点(举例)
GET 获取资源 1. 验证返回状态码为 200。
2. 验证响应体数据结构正确。
3. 测试查询参数(如分页、过滤)。
POST 创建新资源 1. 验证成功创建后返回 201 Created。
2. 验证响应头中包含新资源的 URI (Location)。
3. 测试请求体数据验证(必填项、格式)。
PUT 整体更新资源 1. 验证更新成功返回 200 或 204。
2. 幂等性测试:发送相同请求多次,资源状态应与一次请求相同。
PATCH 部分更新资源 1. 验证仅更新了指定字段,其他字段不变。
2. 测试支持的格式(如 JSON Patch)。
DELETE 删除资源 1. 验证成功删除返回 200 或 204。
2. 删除后再次 GET 该资源应返回 404。
3. 幂等性测试:第二次删除可能返回 404,但资源状态“已删除”不变。

举例说明:
假设要更新用户邮箱。

  • 正确: PATCH /users/123 with body {"email": "new@example.com"}
  • 错误(且测试困难): POST /users/123/updateEmail with body {"email": "new@example.com"}。这个设计模糊了操作语义,测试时难以预测其幂等性。

3. 使用标准的 HTTP 状态码

规范理解:
API 必须通过 HTTP 状态码清晰地传达请求结果。这比在响应体中自定义错误码要直观和标准得多。

  • 测试意义:
    • 自动化脚本简化: 测试脚本可以直接根据状态码判断成功与否,无需深入解析响应体。
    • 明确的场景分类: 状态码将测试用例自然划分为成功、客户端错误、服务器错误等类别。

常见状态码:

状态码 含义 测试场景举例
200 OK 请求成功 GET, PUT, PATCH 成功。
201 Created 资源创建成功 POST 成功后,检查 Location 头。
204 No Content 请求成功,无返回体 DELETE 成功,或 PUT/PATCH 成功但无需返回数据。
400 Bad Request 客户端请求错误 发送格式错误的 JSON 或缺少必填字段。
401 Unauthorized 未认证 未提供 Token 或 Token 无效。
403 Forbidden 无权限 用户认证成功但无权访问该资源。
404 Not Found 资源不存在 请求了不存在的用户 ID。
409 Conflict 资源状态冲突 尝试创建已存在的用户名。
422 Unprocessable Entity 请求格式正确,但语义错误 邮箱格式验证失败。
500 Internal Server Error 服务器内部错误 测试需要监控和报告此类错误。

举例说明:
创建用户时,用户名已存在。

  • 良好设计: 返回 409 Conflict,并在响应体中提供更详细的错误信息,如 {"error": "Username already exists"}
  • 不良设计: 返回 200 OK,但在响应体中用 {"code": 1001, "msg": "失败"} 表示。这会让自动化测试脚本变得复杂。

4. 数据过滤、分页和搜索

规范理解:
对于返回集合的 API(如 GET /articles),必须支持过滤、分页和排序,以避免返回海量数据。

  • 测试意义:
    • 性能测试关键: 分页是性能测试的基础,没有分页的列表 API 无法进行有效的压力测试。
    • 功能完整性: 需要测试各种查询条件的组合。

举例说明:

  • 分页: GET /articles?page=2&limit=20
    • 测试:验证返回的文章数量是否为 20 条,是否是从第 21 条开始的数据。
  • 过滤: GET /articles?author=john&category=tech
    • 测试:验证返回的文章作者都是 john,且类别都是 tech。
  • 排序: GET /articles?sort=-created_at- 表示降序)
    • 测试:验证返回的文章是按创建时间由新到旧排列的。

5. 版本管理

规范理解:
API 必然会发生变更,必须有明确的版本策略,通常通过 URI 或 HTTP 头(如 Accept)来区分。

  • 测试意义:
    • 测试范围清晰: 测试人员可以明确知道当前测试的是哪个版本的 API。
    • 兼容性测试: 需要测试新版本是否向后兼容,或者旧版本客户端是否会被优雅地处理(如提示升级)。

举例说明:

  • URI 版本控制: GET /v1/usersGET /v2/users
    • 测试:需要为 v1 和 v2 分别编写和维护测试用例。
  • Accept 头版本控制: Accept: application/vnd.myapi.v1+json
    • 测试:在测试脚本中需要构造不同的 HTTP 头部。

6. 统一响应体格式

规范理解:
成功和错误的响应体格式应该保持一致,便于客户端解析。

  • 测试意义:
    • 断言标准化: 测试脚本可以对响应结构进行统一的断言。
    • 错误处理自动化: 可以编写通用的错误响应解析逻辑。

举例说明:

  • 成功响应:
    {
      "data": {
        "id": 123,
        "name": "John Doe",
        "email": "john@example.com"
      }
    }
    
  • 错误响应:
    {
      "error": {
        "code": "INVALID_EMAIL",
        "message": "The provided email is invalid.",
        "details": {...}
      }
    }
    
    测试时,可以轻松地通过 response.body.error.code 来断言特定的错误类型。

总结:对测试工程师的价值

一个严格遵守 RESTful 规范的 API,对于测试工作而言意味着:

  1. 可预测性: 通过 URI 和方法就能准确预测 API 的行为,降低了学习和沟通成本。
  2. 可测试性: 清晰的语义、标准的状态码和统一的格式,使得手动测试和自动化测试都更容易设计和实现。
  3. 可维护性: 当 API 扩展或修改时,遵循规范的变更(如增加版本)不会对现有测试用例造成毁灭性影响。
  4. 健壮性: 对幂等性、安全性的支持,使得系统在面对网络不稳定、客户端重试等场景时更加健壮,这也直接减少了测试需要关注的“怪异”场景。

因此,一个优秀的测试工程师不应只被动地验证功能,而应主动地将这些设计规范作为测试审查的一部分,在 API 设计阶段就提出改进建议,从源头提升软件质量。


使用Postman进行手动API测试

什么是Postman?

Postman 是一个流行的API开发测试工具,用于测试、开发和文档化API。

这里就省略Postman的安装步骤了,有需要的小伙伴可以自行百度安装,非常简单。如果需要汉化版的Postman,可以联系我。

发送第一个API请求

创建新请求

  • 点击左上角 "New" 按钮(或者使用快捷鍵Ctrl + N)

  • 选择 "HTTP Request"
    image


配置及请求

image

操作说明:

  • API命名(图1):自定义一个API名称。

  • 请求方式(图2):下拉菜单选择 GET、POST、PUT、DELETE 等。

  • 请求URL(图3):需要请求的API链接。

  • 请求体(图5):请求时需要携带的请求头和API链接需要的请求参数。

  • 请求参数(图6):具体的参数信息。

  • 发送请求(图4):发送请求。

  • 响应结果(图7):HTTP 状态码(如 200 OK),请求耗时、响应大小。

  • 响应体(图8):Body(响应内容)、Cookie(缓存)、Header(服务器返回的响应头部信息)、测试结果(如果上面有测试脚本,则会显示对应的结果信息)。

  • 响应区域(图9):返回的响应内容。


环境变量与测试脚本

环境变量概念

环境变量允许你在不同环境(开发、测试、生产)中使用不同的配置值。


创建环境变量

  • 点击右上角眼睛图标

  • 选择 "Manage Environments"

  • 点击 "Add"

  • 输入环境名称和变量

image

如图:我设置了两个测试环境的变量:auth 和 code。


在请求中使用变量

```
GET {{base_url}}/users/{{user_id}}
Authorization: Bearer {{token}}
```

image

如上图

  • 使用环境变量(图1):使用 {{}} 双花括号语法引用变量。
  • 环境切换(图2):点击右上角环境选择器,在不同环境间快速切换。

测试脚本

作为测试人员,在使用Postman这个工具的时候,最重要的就是使用测试脚本,测试脚本的运用也意味着你是否符合自动化测试还是仅仅是功能测试。

image

如上图,我的需求是:如果响应状态码是200,则获取响应内容中的token_type字段和access_token字段,拼接字符串为token_type+空格+access_token ,并加入名称为:token的环境变量中,如果响应状态码不是200,则显示错误信息。

// 测试脚本:处理认证响应并设置token环境变量
pm.test("处理认证响应", function () {
    // 检查响应状态码
    if (pm.response.code === 200) {
        // 状态码为200时的处理逻辑
        pm.test("状态码为200 - 成功", function () {
            pm.response.to.have.status(200);
        });
        
        try {
            // 解析JSON响应
            const responseData = pm.response.json();
            
            // 检查必需的字段是否存在
            pm.test("响应包含token_type字段", function () {
                pm.expect(responseData).to.have.property('token_type');
            });
            
            pm.test("响应包含access_token字段", function () {
                pm.expect(responseData).to.have.property('access_token');
            });
            
            // 获取token字段值
            const tokenType = responseData.token_type;
            const accessToken = responseData.access_token;
            
            // 验证字段值不为空
            pm.test("token_type不为空", function () {
                pm.expect(tokenType).to.be.a('string').and.to.not.be.empty;
            });
            
            pm.test("access_token不为空", function () {
                pm.expect(accessToken).to.be.a('string').and.to.not.be.empty;
            });
            
            // 拼接token字符串
            const fullToken = tokenType + " " + accessToken;
            
            // 设置环境变量
            pm.environment.set("token", fullToken);
            
            // 验证环境变量设置成功
            pm.test("token环境变量已设置", function () {
                const savedToken = pm.environment.get("token");
                pm.expect(savedToken).to.equal(fullToken);
            });
            
            // 在控制台输出成功信息(可选)
            console.log("Token设置成功:", fullToken);
            console.log("环境变量'token'已更新");
            
        } catch (error) {
            // JSON解析错误处理
            pm.test("响应JSON格式正确", function () {
                pm.expect.fail("响应不是有效的JSON格式: " + error.message);
            });
        }
        
    } else {
        // 状态码不是200时的处理逻辑
        pm.test("请求失败 - 状态码: " + pm.response.code, function () {
            // 显示详细的错误信息
            const errorMessage = "请求失败!状态码: " + pm.response.code + 
                               ", 状态文本: " + pm.response.statusText;
            
            // 在测试结果中显示错误
            pm.expect.fail(errorMessage);
            
            // 尝试获取响应中的错误信息(如果有)
            try {
                const errorResponse = pm.response.json();
                if (errorResponse.error) {
                    console.error("错误详情:", errorResponse.error);
                }
                if (errorResponse.message) {
                    console.error("错误消息:", errorResponse.message);
                }
            } catch (e) {
                // 如果不是JSON格式,显示原始文本
                console.error("错误响应:", pm.response.text());
            }
            
            // 清除可能存在的旧token环境变量(可选)
            pm.environment.unset("token");
            console.log("已清除token环境变量");
        });
    }
});

// 额外的测试:验证整体响应结构
pm.test("响应时间检查", function () {
    pm.expect(pm.response.responseTime).to.be.below(5000); // 5秒内
});

// 响应头检查
pm.test("Content-Type头存在", function () {
    pm.response.to.have.header("Content-Type");
});

脚本功能说明:

  1. 状态码200时的处理

    • 验证响应包含token_typeaccess_token字段
    • 拼接格式:token_type access_token
    • 保存到环境变量token
    • 验证保存成功
  2. 状态码非200时的处理

    • 显示详细的错误信息(状态码和状态文本)
    • 尝试解析错误响应体
    • 清除可能存在的旧token环境变量
  3. 错误处理

    • JSON解析异常处理
    • 字段缺失处理
    • 空值验证

验证环境变量保存成功
image

常用的测试断言

  • 响应状态码验证
  // 精确状态码验证
  pm.test("Status code is 200", function () {
      pm.response.to.have.status(200);
  });

  // 状态码范围验证
  pm.test("Successful POST request", function () {
      pm.expect(pm.response.code).to.be.oneOf([200, 201, 202]);
  });

  // 状态码文本验证
  pm.test("Status code name has string", function () {
      pm.response.to.have.status("OK");
  });

  • 响应头验证
  // 检查 Content-Type
  pm.test("Content-Type is present", function () {
      pm.response.to.have.header("Content-Type");
  });

  pm.test("Content-Type is application/json", function () {
      pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");
  });

  // 检查自定义头
  pm.test("X-RateLimit-Limit header exists", function () {
      pm.response.to.have.header("X-RateLimit-Limit");
  });

  • 响应体验证
  // JSON 响应验证
  pm.test("Response has correct structure", function () {
      var response = pm.response.json();
      pm.expect(response).to.be.an("object");
      pm.expect(response).to.have.all.keys("id", "name", "email", "createdAt");
  });

  // 数组响应验证
  pm.test("Array contains expected items", function () {
      var response = pm.response.json();
      pm.expect(response).to.be.an("array");
      pm.expect(response).to.have.length.above(0);
      
      // 验证数组中的对象结构
      response.forEach(function(item) {
          pm.expect(item).to.have.property("id");
          pm.expect(item).to.have.property("name");
      });
  });

  // 字符串包含验证
  pm.test("Body matches string", function () {
      pm.expect(pm.response.text()).to.include("success");
  });

  • 复杂数据-数据类型验证
    pm.test("Data types are correct", function () {
      var jsonData = pm.response.json();
      pm.expect(jsonData.id).to.be.a("number");
      pm.expect(jsonData.name).to.be.a("string");
      pm.expect(jsonData.isActive).to.be.a("boolean");
      pm.expect(jsonData.tags).to.be.an("array");
      pm.expect(jsonData.metadata).to.be.an("object");
    });

预请求脚本

  • 动态生成数据
  // 生成随机数据
  const randomId = Math.floor(Math.random() * 1000);
  const timestamp = new Date().toISOString();

  // 设置环境变量
  pm.environment.set("random_user_id", randomId);
  pm.environment.set("current_timestamp", timestamp);

  // 生成随机字符串
  function generateRandomString(length) {
      let result = '';
      const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      for (let i = 0; i < length; i++) {
          result += characters.charAt(Math.floor(Math.random() * characters.length));
      }
      return result;
  }

  pm.environment.set("random_email", `test${randomId}@example.com`);
  pm.environment.set("random_name", `User_${generateRandomString(8)}`);

  • token自动刷新
  // 检查 token 是否过期
  const tokenExpiry = pm.environment.get("token_expiry");
  const currentTime = new Date().getTime();

  if (!tokenExpiry || currentTime > tokenExpiry) {
      // 执行 token 刷新逻辑
      pm.sendRequest({
          url: pm.environment.get("auth_url") + "/refresh",
          method: "POST",
          header: {
              "Content-Type": "application/json",
              "Authorization": "Bearer " + pm.environment.get("refresh_token")
          }
      }, function (err, response) {
          if (!err) {
              const newToken = response.json().access_token;
              const expiresIn = response.json().expires_in;
              
              pm.environment.set("access_token", newToken);
              pm.environment.set("token_expiry", currentTime + (expiresIn * 1000));
          }
      });
  }

  • 请求参数处理
    // 动态构建查询参数
    const queryParams = {
        page: 1,
        limit: 20,
        sort: "createdAt",
        order: "desc"
    };

    // 根据条件添加参数
    if (pm.environment.get("filter_status")) {
        queryParams.status = pm.environment.get("filter_status");
    }

    // 设置到请求URL
    const url = new URL(pm.request.url);
    Object.keys(queryParams).forEach(key => {
        url.searchParams.set(key, queryParams[key]);
    });

    pm.request.url = url.toString();

使用 Newman 运行Postman集合

什么是Newman?

Newman 是 Postman 的命令行集合运行工具。它将 API 测试集成到 CI/CD 管道中,直接通过命令行或脚本运行和测试 Postman 集合,非常适合自动化环境。实现自动化测试。


第一步:准备工作 - 导出集合和环境变量

在命令行中运行之前,你需要将 Postman 中的资源导出为 JSON 文件。

  1. 导出集合
    • 在 Postman 中,进入你的 Collections 标签页。
    • 找到你想要运行的集合,点击右侧的 ... 按钮。
    • 选择 Export
    • 在弹出的对话框中,推荐选择 Collection v2.1 作为导出格式。
    • 将集合文件(例如 My-Collection.postman_collection.json)保存到你的项目文件夹中。

image image

  1. 导出环境变量(如果使用)
    • 如果你在集合中使用了环境变量(如 {{base_url}}),你需要同样导出环境。
    • 点击 Postman 右上角的眼睛图标,然后点击环境旁边的 ... 按钮。
    • 选择 Export
    • 将环境文件(例如 My-Environment.postman_environment.json)保存到同一个项目文件夹中。

第二步:安装 Newman

Newman 基于 Node.js,因此你需要先安装 Node.js。安装完成后,使用 npm(Node.js 的包管理器)来安装 Newman。

  1. 安装 Node.js

    • 访问 Node.js 官网 下载并安装 LTS 版本。这会同时安装 nodenpm
  2. 安装 Newman

    • 打开你的终端(Command Prompt, PowerShell, Terminal 等)。
    • 运行以下命令进行全局安装,这样你就可以在任何地方使用 newman 命令了。
    npm install -g newman
    
    • 注意:在 macOS 或 Linux 上,你可能需要在命令前加上 sudo 来获取权限:sudo npm install -g newman

第三步:基本运行命令

安装完成后,你就可以使用 newman run 命令来执行集合了。

最基本语法:

newman run <collection-file>

示例:

假设你的集合文件名为 My-Collection.postman_collection.json,并且它在当前终端目录下。

newman run My-Collection.postman_collection.json

这将以默认配置运行集合,并在终端中输出结果摘要。


第四步:常用命令选项和功能

Newman 的强大之处在于其丰富的命令行选项。

1. 指定环境变量文件

使用 -e--environment 选项。

newman run My-Collection.postman_collection.json -e My-Environment.postman_environment.json

2. 使用数据文件进行数据驱动测试

使用 -d--iteration-data 选项。数据文件可以是 JSON 或 CSV 格式。

# 使用 JSON 数据文件
newman run My-Collection.postman_collection.json -d data.json

# 使用 CSV 数据文件
newman run My-Collection.postman_collection.json -d data.csv

3. 指定迭代次数

使用 -n--iteration-count 选项。当与数据文件一起使用时,它会遍历数据文件的所有行。如果没有数据文件,它会将整个集合运行指定的次数。

# 运行集合 5 次
newman run My-Collection.postman_collection.json -n 5

4. 生成测试报告

Newman 支持多种格式的报告,通过 -r--reporters 选项指定。

  • CLI:默认报告器,在控制台输出彩色结果。
  • JSON:将详细结果输出到 JSON 文件。
  • HTML:生成一个美观的 HTML 报告(需要额外安装)。
  • JUnit:生成 JUnit 格式的 XML 报告,用于 Jenkins 等 CI 工具。

生成 JSON 和 CLI 报告:

newman run My-Collection.postman_collection.json -r cli,json

生成 HTML 报告:

首先,需要安装 newman-reporter-html 或 newman-reporter-htmlextra:

npm install -g newman-reporter-html

npm install -g newman-reporter-htmlextra

然后运行命令:

newman run My-Collection.postman_collection.json -r html,cli

默认的 HTML 报告会生成在 newman/ 目录下。

5. 全局变量和延迟

  • 设置全局变量:使用 -g--globals
    newman run ... -g My-Globals.postman_globals.json
    
  • 设置请求延迟(毫秒):使用 --delay-request
    newman run ... --delay-request 1000 # 每个请求间隔 1 秒
    

第五步:综合示例

附上一个结合了上述所有常用功能的综合命令示例:

newman run test.postman_collection.json 
  -e test.postman_environment.json 
  -d user_query_params.csv 
  -n 4 
  --reporters htmlextra,json 
  --reporter-json-export results\json-results.json
  --reporter-htmlextra-export results\html-results.html
  --delay-request 500

解释:

  • run:运行指定的集合文件。
  • -e:使用指定的环境变量文件。
  • -d:使用 CSV 文件进行数据驱动测试。
  • -n:迭代4次。
  • -r/--reporters htmlextra,json:同时生成两种格式的报告。
  • --reporter-json-export:指定 JSON 报告的输出路径。
  • --reporter-htmlextra-export:指定 HTML 报告的输出路径。
  • --delay-request:在每个请求之间设置 500 毫秒的延迟。

上述命令执行完后,会在本地生成两个文件

image

image

上图就是生成的报告( html-results.html )HTML视图。

在 CI/CD 中集成(例如 GitHub Actions)

GitHub Actions 示例

name: API Tests

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  api-tests:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Setup Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '16'
    
    - name: Install Newman
      run: |
        npm install -g newman
        npm install -g newman-reporter-html
    
    - name: Run API Tests
      run: |
        newman run collections/user-api.json \
          -e environments/production.json \
          -r html,json \
          --reporter-html-export reports/api-test-results.html
    
    - name: Upload Test Results
      uses: actions/upload-artifact@v2
      with:
        name: api-test-reports
        path: reports/

Jenkins Pipeline 示例

pipeline {
    agent any
    
    stages {
        stage('API Tests') {
            steps {
                script {
                    sh '''
                    npm install -g newman
                    newman run collections/api-tests.json \
                      -e environments/staging.json \
                      -r html,json,junit \
                      --reporter-html-export test-results/api-report.html
                    '''
                }
            }
            
            post {
                always {
                    junit 'test-results/*.xml'
                    publishHTML([
                        allowMissing: false,
                        alwaysLinkToLastBuild: true,
                        keepAll: true,
                        reportDir: 'test-results',
                        reportFiles: 'api-report.html',
                        reportName: 'API Test Report'
                    ])
                }
            }
        }
    }
}

总结

使用 Newman 运行 Postman 集合的核心步骤是:

  1. 导出集合和环境为 JSON 文件。
  2. 安装 Node.js 和 Newman。
  3. 运行 newman run 命令,并通过丰富的选项来指定环境、数据、报告等。

通过掌握 Newman,你可以轻松地将 Postman 的测试能力从图形化界面扩展到自动化和持续集成流程中。

API测试工具除了Postman以外,还有个新出来的APifox,个人感觉还是挺不错的,比Postman简单方便一点。
到这里,测试工程师所需要掌握的API测试就结束了。喜欢的小伙伴动动你们的小手,帮忙点点支持。

posted @ 2025-11-26 18:10  喜欢你的眉眼微笑i  阅读(16)  评论(0)    收藏  举报