# Script API 参考手册 本文档提供了 EasyPostman 脚本功能的完整 API 参考。所有 API 均基于实际代码整理,确保准确可用。 ## 目录 - [全局对象](#全局对象) - [pm 对象](#pm-对象) - [pm.environment - 环境变量](#pmenvironment---环境变量) - [全局变量(Global Variables)](#全局变量global-variables) - [pm.variables - 临时变量](#pmvariables---临时变量) - [pm.request - 请求对象](#pmrequest---请求对象) - [pm.response - 响应对象](#pmresponse---响应对象) - [pm.cookies - Cookie 管理](#pmcookies---cookie-管理) - [pm.expect - 断言](#pmexpect---断言) - [pm.test - 测试](#pmtest---测试) - [外部数据源脚本 API](#外部数据源脚本-api) - [console - 控制台](#console---控制台) - [内置 JavaScript 库](#内置-javascript-库) - [完整示例](#完整示例) - [注意事项](#注意事项) - [快速参考](#快速参考) - [参考资源](#参考资源) --- ## 全局对象 ### pm 主要的 Postman API 对象,提供了所有脚本功能的访问入口。 --- ## pm 对象 ### 方法列表 | 方法 | 说明 | 示例 | |------------------------------------|--------------------|------------------------------------------------------| | `pm.test(name, fn)` | 定义一个测试 | `pm.test("状态码是 200", () => {})` | | `pm.expect(value)` | 创建断言 | `pm.expect(200).to.equal(200)` | | `pm.uuid()` | 生成 UUID | `pm.uuid()` | | `pm.getTimestamp()` | 获取当前时间戳(毫秒) | `pm.getTimestamp()` | | `pm.getResponseCookie(name)` | 获取响应中的 Cookie | `pm.getResponseCookie('sessionId')` | | `pm.sendRequest(options, callback)` | 在脚本中发送额外 HTTP 请求 | `pm.sendRequest({ url: 'https://api.com' }, (err, resp) => {})` | ### 外部数据源对象 | 对象 | 说明 | 常用方法 | |------|------|----------| | `pm.plugin("redis")` | Redis 读写与查询 | `execute(options)`、`query(options)` | | `pm.plugin("kafka")` | Kafka 查询、发消息、消费消息 | `listTopics(options)`、`send(options)`、`poll(options)` | | `pm.elasticsearch` | Elasticsearch 请求与查询 | `request(options)`、`query(options)` | | `pm.influxdb` | InfluxDB Flux / InfluxQL 查询与写入 | `query(options)`、`write(options)`、`request(options)` | ### pm.sendRequest() `pm.sendRequest()` 已支持在脚本中主动发起额外 HTTP 请求,适用于前置脚本里刷新 token、拉取签名、预热上下文数据等场景。 回调函数签名为 `(err, response)`: - `err`:请求失败时返回错误对象,包含 `message`、`name` - `response`:成功时返回响应包装对象,可使用 `response.code`、`response.status`、`response.headers.get()`、`response.text()`、`response.json()` 支持的请求格式: - 直接传 URL 字符串,默认 `GET` - 传对象:`url`、`method`、`header`、`body` - `body.mode` 当前支持 `raw`、`formdata`、`urlencoded` 示例: ```javascript pm.sendRequest({ url: 'https://httpbin.org/get?source=easy-postman&from=pre-script', method: 'GET', header: { 'X-Debug-Token': pm.environment.get('debugToken') || 'demo-token' } }, function (err, response) { if (err) { console.error('请求失败:', err.message); return; } const data = response.json(); pm.environment.set('lastHttpbinUrl', data.url || ''); pm.environment.set('lastHttpbinSource', data.args?.source || ''); console.log('httpbin args:', data.args); }); ``` --- ## pm.environment - 环境变量 环境变量的读写操作。 ### 方法列表 | 方法 | 参数 | 返回值 | 说明 | 示例 | |-------------------|-------------------------|---------|------------|-----------------------------------------| | `get(key)` | key: String | String | 获取环境变量 | `pm.environment.get('token')` | | `set(key, value)` | key: String, value: Any | void | 设置环境变量 | `pm.environment.set('token', 'abc123')` | | `unset(key)` | key: String | void | 删除环境变量 | `pm.environment.unset('token')` | | `has(key)` | key: String | Boolean | 检查环境变量是否存在 | `pm.environment.has('token')` | | `clear()` | 无 | void | 清空所有环境变量 | `pm.environment.clear()` | --- ## 全局变量(Global Variables) 全局变量通过 `pm.globals` 访问,生命周期独立于当前请求。 ### 重要说明 - ✅ **支持**:`pm.globals.set()` / `pm.globals.get()` - ✅ **支持**:`pm.globals.has()` / `pm.globals.unset()` / `pm.globals.clear()` / `pm.globals.toObject()` ### 方法列表 | 方法 | 参数 | 说明 | 示例 | |------|------|------|------| | `pm.globals.set(key, value)` | key: String
value: Any | 设置全局变量 | `pm.globals.set('baseUrl', 'https://api.com')` | | `pm.globals.get(key)` | key: String | 获取全局变量 | `pm.globals.get('baseUrl')` | | `pm.globals.has(key)` | key: String | 检查全局变量是否存在 | `pm.globals.has('baseUrl')` | | `pm.globals.unset(key)` | key: String | 删除全局变量 | `pm.globals.unset('baseUrl')` | | `pm.globals.clear()` | 无 | 清空全局变量 | `pm.globals.clear()` | ### 使用示例 ```javascript pm.globals.set('apiKey', 'abc123'); pm.globals.set('timeout', 5000); const apiKey = pm.globals.get('apiKey'); console.log('API Key:', apiKey); ``` --- ## pm.variables - 临时变量 临时变量管理,仅在当前请求执行过程中有效(不会持久化)。 ### 方法列表 | 方法 | 参数 | 返回值 | 说明 | 示例 | |-------------------|-------------------------|---------|-----------------|-----------------------------------| | `get(key)` | key: String | String | 获取临时变量 | `pm.variables.get('userId')` | | `set(key, value)` | key: String, value: Any | void | 设置临时变量 | `pm.variables.set('userId', 123)` | | `has(key)` | key: String | Boolean | 检查临时变量是否存在 | `pm.variables.has('userId')` | | `unset(key)` | key: String | void | 删除临时变量 | `pm.variables.unset('userId')` | | `clear()` | 无 | void | 清空所有临时变量 | `pm.variables.clear()` | | `toObject()` | 无 | Object | 获取所有临时变量对象(键值对) | `pm.variables.toObject()` | --- ## pm.request - 请求对象 访问和操作当前 HTTP 请求的信息(主要在 Pre-request 脚本中使用)。 ### 属性 | 属性 | 类型 | 说明 | 示例 | |-------------------|---------------|-------------------|------------------------------| | `id` | String | 请求唯一标识 | `pm.request.id` | | `url` | UrlWrapper | 请求 URL 对象 | `pm.request.url` | | `method` | String | HTTP 方法 | `pm.request.method` | | `headers` | JsListWrapper | 请求头列表 | `pm.request.headers` | | `body` | String | 请求体内容 | `pm.request.body` | | `formData` | JsListWrapper | 表单数据列表(multipart) | `pm.request.formData` | | `urlencoded` | JsListWrapper | URL 编码表单数据列表 | `pm.request.urlencoded` | | `params` | JsListWrapper | URL 查询参数列表 | `pm.request.params` | | `isMultipart` | Boolean | 是否为 multipart 请求 | `pm.request.isMultipart` | | `followRedirects` | Boolean | 是否跟随重定向 | `pm.request.followRedirects` | | `logEvent` | Boolean | 是否记录事件日志 | `pm.request.logEvent` | ### URL 对象方法 | 方法 | 返回值 | 说明 | 示例 | |----------------------|--------|--------------|-------------------------------------| | `toString()` | String | 获取完整 URL 字符串 | `pm.request.url.toString()` | | `getHost()` | String | 获取主机名 | `pm.request.url.getHost()` | | `getPath()` | String | 获取路径 | `pm.request.url.getPath()` | | `getQueryString()` | String | 获取查询字符串 | `pm.request.url.getQueryString()` | | `getPathWithQuery()` | String | 获取路径和查询字符串 | `pm.request.url.getPathWithQuery()` | ### URL Query 对象 访问查询参数:`pm.request.url.query` | 方法 | 返回值 | 说明 | 示例 | |---------|-------|----------|------------------------------| | `all()` | Array | 获取所有查询参数 | `pm.request.url.query.all()` | ### Headers/FormData/Urlencoded/Params 集合方法 这些集合都是 `JsListWrapper` 类型,支持以下方法: | 方法 | 参数 | 返回值 | 说明 | 示例 | |--------------------------|----------------------|---------|-----------|--------------------------------------------------------------| | `add(item)` | item: Object | void | 添加一项 | `pm.request.headers.add({key: 'X-Custom', value: 'test'})` | | `remove(keyOrPredicate)` | key: String/Function | void | 删除一项 | `pm.request.headers.remove('X-Custom')` | | `upsert(item)` | item: Object | void | 更新或插入一项 | `pm.request.headers.upsert({key: 'X-Custom', value: 'new'})` | | `get(key)` | key: String | String | 获取指定键的值 | `pm.request.headers.get('Content-Type')` | | `has(key)` | key: String | Boolean | 检查是否存在指定键 | `pm.request.headers.has('Authorization')` | | `all()` | 无 | Array | 获取所有项 | `pm.request.headers.all()` | | `count()` | 无 | Number | 获取项数 | `pm.request.headers.count()` | | `clear()` | 无 | void | 清空所有项 | `pm.request.headers.clear()` | | `each(callback)` | callback: Function | void | 遍历每一项 | `pm.request.headers.each(h => console.log(h))` | ### 使用示例 ```javascript // 添加请求头 pm.request.headers.add({ key: "Authorization", value: "Bearer " + pm.environment.get("token") }); // 添加查询参数 pm.request.params.add({ key: "timestamp", value: Date.now().toString() }); // 添加表单数据 pm.request.formData.add({ key: "username", value: "john" }); // 获取 URL 信息 console.log("Host:", pm.request.url.getHost()); console.log("Path:", pm.request.url.getPath()); ``` --- ## pm.response - 响应对象 访问 HTTP 响应的信息(仅在 Post-request 脚本中可用)。 ### 属性 | 属性 | 类型 | 说明 | 示例 | |----------------|-------------------|-----------|----------------------------------------------| | `code` | Number | HTTP 状态码 | `pm.response.code` | | `status` | String | HTTP 状态文本 | `pm.response.status` | | `headers` | Headers | 响应头对象 | `pm.response.headers` | | `responseTime` | Number | 响应时间(毫秒) | `pm.response.responseTime` | | `to` | ResponseAssertion | 链式断言语法支持 | `pm.response.to.have.status(200)` | | `have` | ResponseAssertion | 链式断言语法支持 | `pm.response.to.have.header('Content-Type')` | | `be` | ResponseAssertion | 链式断言语法支持 | 用于链式调用 | ### 方法列表 | 方法 | 返回值 | 说明 | 示例 | |----------|--------------|---------------|----------------------| | `text()` | String | 获取响应体文本 | `pm.response.text()` | | `json()` | Object | 获取响应体 JSON 对象 | `pm.response.json()` | | `size()` | ResponseSize | 获取响应大小信息 | `pm.response.size()` | ### 响应断言方法 | 方法 | 说明 | 示例 | |------------------------|-------------|---------------------------------------------------------| | `to.have.status(code)` | 断言状态码 | `pm.response.to.have.status(200)` | | `to.have.header(name)` | 断言包含响应头 | `pm.response.to.have.header('Content-Type')` | | `to.be.below(ms)` | 断言响应时间小于指定值 | `pm.expect(pm.response.responseTime).to.be.below(1000)` | ### Headers 对象方法 访问响应头:`pm.response.headers` | 方法 | 参数 | 返回值 | 说明 | 示例 | |------------------|--------------------|---------|-----------|-------------------------------------------------| | `get(name)` | name: String | String | 获取响应头值 | `pm.response.headers.get('Content-Type')` | | `has(name)` | name: String | Boolean | 检查响应头是否存在 | `pm.response.headers.has('Set-Cookie')` | | `count()` | 无 | Number | 获取响应头数量 | `pm.response.headers.count()` | | `all()` | 无 | Array | 获取所有响应头 | `pm.response.headers.all()` | | `each(callback)` | callback: Function | void | 遍历所有响应头 | `pm.response.headers.each(h => console.log(h))` | ### ResponseSize 对象 `pm.response.size()` 返回的对象包含以下属性: | 属性 | 类型 | 说明 | |----------|--------|-----------| | `body` | Number | 响应体大小(字节) | | `header` | Number | 响应头大小(字节) | | `total` | Number | 总大小(字节) | ### 使用示例 ```javascript // 获取响应数据 const jsonData = pm.response.json(); console.log("Status:", pm.response.status); console.log("Code:", pm.response.code); // 断言状态码 pm.response.to.have.status(200); // 断言响应头 pm.response.to.have.header('Content-Type'); // 获取响应头 const contentType = pm.response.headers.get('Content-Type'); // 获取响应大小 const size = pm.response.size(); console.log("响应体大小:", size.body, "bytes"); ``` --- ## pm.cookies - Cookie 管理 管理和访问 Cookie。 ### 方法列表 | 方法 | 参数 | 返回值 | 说明 | 示例 | |---------------|-----------------------|-----------|-----------------------|-----------------------------------------------| | `get(name)` | name: String | Cookie | 获取指定名称的 Cookie | `pm.cookies.get('sessionId')` | | `set(cookie)` | cookie: Cookie/String | void | 设置 Cookie | `pm.cookies.set({name: 'key', value: 'val'})` | | `getAll()` | 无 | Array | 获取所有 Cookie | `pm.cookies.getAll()` | | `has(name)` | name: String | Boolean | 检查 Cookie 是否存在 | `pm.cookies.has('sessionId')` | | `toObject()` | 无 | Object | 获取所有 Cookie 对象(键值对形式) | `pm.cookies.toObject()` | | `jar()` | 无 | CookieJar | 获取 CookieJar 对象 | `pm.cookies.jar()` | ### CookieJar 对象 CookieJar 用于跨域管理 Cookie,通过 `pm.cookies.jar()` 获取。 #### 方法列表 | 方法 | 参数 | 说明 | 示例 | |------------------------------|------------------------------------------------------------|---------------------|-----------------------------------------| | `set(url, cookie, callback)` | url: String
cookie: String/Object
callback: Function | 设置指定 URL 的 Cookie | `jar.set(url, 'key=value', callback)` | | `get(url, name, callback)` | url: String
name: String
callback: Function | 获取指定 URL 的 Cookie | `jar.get(url, 'sessionId', callback)` | | `getAll(url, callback)` | url: String
callback: Function | 获取指定 URL 的所有 Cookie | `jar.getAll(url, callback)` | | `unset(url, name, callback)` | url: String
name: String
callback: Function | 删除指定 URL 的 Cookie | `jar.unset(url, 'sessionId', callback)` | | `clear(url, callback)` | url: String
callback: Function | 清空指定 URL 的所有 Cookie | `jar.clear(url, callback)` | ### Cookie 对象属性 | 属性 | 类型 | 说明 | |------------|---------|---------------| | `name` | String | Cookie 名称 | | `value` | String | Cookie 值 | | `domain` | String | Cookie 域 | | `path` | String | Cookie 路径 | | `expires` | String | 过期时间 | | `maxAge` | Number | 最大存活时间(秒) | | `httpOnly` | Boolean | 是否仅 HTTP | | `secure` | Boolean | 是否安全传输(HTTPS) | | `sameSite` | String | SameSite 属性 | ### 使用示例 ```javascript // 获取 Cookie const sessionId = pm.cookies.get('sessionId'); if (sessionId) { console.log('Session ID:', sessionId.value); } // 设置 Cookie pm.cookies.set({ name: 'myToken', value: 'abc123', domain: 'example.com', path: '/' }); // 检查 Cookie 是否存在 if (pm.cookies.has('sessionId')) { console.log('Session cookie exists'); } // 获取所有 Cookie const allCookies = pm.cookies.getAll(); console.log('Total cookies:', allCookies.length); // 使用 CookieJar 跨域设置 Cookie const jar = pm.cookies.jar(); jar.set('https://api.example.com', 'token=xyz', (error, cookie) => { if (error) { console.error('设置 cookie 失败:', error); } else { console.log('Cookie 设置成功:', cookie); } }); // 使用 CookieJar 获取 Cookie jar.get('https://api.example.com', 'token', (error, cookie) => { if (!error && cookie) { console.log('Token:', cookie.value); } }); ``` --- ## pm.expect - 断言 使用链式断言进行测试(类 Chai.js 风格)。 ### 链式语法支持 | 链式属性 | 说明 | |--------|-------| | `to` | 链式连接词 | | `be` | 链式连接词 | | `have` | 链式连接词 | ### 支持的断言方法 | 断言 | 参数 | 说明 | 示例 | |----------------------|------------------------------|----------------------|------------------------------------------------| | `equal(value)` | value: Any | 严格相等(深度比较) | `pm.expect(200).to.equal(200)` | | `eql(value)` | value: Any | 深度相等(与 equal 相同) | `pm.expect({a: 1}).to.eql({a: 1})` | | `include(substring)` | substring: String | 包含子串 | `pm.expect('hello world').to.include('hello')` | | `property(key)` | key: String | 包含属性(仅支持 Map/Object) | `pm.expect(obj).to.have.property('id')` | | `match(regex)` | regex: String/Pattern/RegExp | 匹配正则表达式 | `pm.expect('hello').to.match(/^h/)` | | `below(number)` | number: Number | 数值小于指定值 | `pm.expect(5).to.be.below(10)` | ### 使用示例 ```javascript // 基本相等断言 pm.test("Status code is 200", function () { pm.expect(pm.response.code).to.equal(200); }); // 深度相等断言 pm.test("Response data matches", function () { const jsonData = pm.response.json(); pm.expect(jsonData).to.eql({status: "success"}); }); // 包含子串 pm.test("Response contains success", function () { pm.expect(pm.response.text()).to.include("success"); }); // 属性存在 pm.test("Response has userId property", function () { const jsonData = pm.response.json(); pm.expect(jsonData).to.have.property('userId'); }); // 正则匹配 pm.test("Email format is correct", function () { pm.expect(email).to.match(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/); }); // 数值比较 pm.test("Response time is acceptable", function () { pm.expect(pm.response.responseTime).to.be.below(1000); }); ``` ### 注意事项 - 当前实现支持的断言方法有限,主要包括:equal、eql、include、property、match、below - 不支持:above、least、most、within、length、keys、members、true、false、null、undefined、ok、empty 等 - 如需更多断言功能,建议使用 pm.test 结合简单的 if 判断 --- ## pm.test - 测试 定义和管理测试用例。 ### 主要方法 #### pm.test(name, function) 定义一个测试用例。 | 参数 | 类型 | 说明 | |------------|----------|--------------------------------------| | `name` | String | 测试名称 | | `function` | Function | 测试函数(可使用 pm.expect 或 pm.response 断言) | ### 使用示例 ```javascript // 定义测试 - 状态码检查 pm.test("状态码是 200", function () { pm.response.to.have.status(200); }); // 定义测试 - 使用 pm.expect pm.test("响应时间小于 500ms", function () { pm.expect(pm.response.responseTime).to.be.below(500); }); // 定义测试 - JSON 数据验证 pm.test("响应包含用户 ID", function () { const jsonData = pm.response.json(); pm.expect(jsonData).to.have.property('userId'); pm.expect(jsonData.userId).to.equal(123); }); // 定义测试 - 响应头检查 pm.test("响应包含 Content-Type", function () { pm.response.to.have.header('Content-Type'); }); ``` --- ## 外部数据源脚本 API 可以在 Pre-request Script 或 Test Script 中直接访问 Redis、Kafka、Elasticsearch、InfluxDB,并结合 `pm.test()` / `pm.expect()` 进行断言。 ### pm.plugin("redis") #### 方法列表 | 方法 | 参数 | 返回值 | 说明 | |------|------|--------|------| | `execute(options)` | `host`、`port`、`db`、`command`、`key`、`args`、`value` | Any | 执行一次 Redis 命令 | | `query(options)` | 同 `execute(options)` | Any | `execute()` 的别名 | #### 示例:写入并断言 Redis ```javascript const redis = pm.plugin('redis'); redis.execute({ host: 'localhost', port: 6379, db: 0, command: 'SET', key: 'user:1001', value: JSON.stringify({id: 1001, name: 'Alice'}) }); const redisValue = redis.query({ host: 'localhost', port: 6379, db: 0, command: 'GET', key: 'user:1001' }); pm.test('Redis 写入成功', function () { pm.expect(redisValue).to.include('Alice'); }); ``` ### pm.plugin("kafka") #### 方法列表 | 方法 | 参数 | 返回值 | 说明 | |------|------|--------|------| | `listTopics(options)` | `bootstrapServers` 等 | `Array` | 获取 topic 列表 | | `send(options)` | `bootstrapServers`、`topic`、`key`、`value`、`headers` | Object | 发送消息 | | `poll(options)` | `bootstrapServers`、`topic`、`groupId`、`pollTimeoutMs` 等 | Array | 拉取消息 | #### 示例:发消息并断言 Kafka ```javascript const kafka = pm.plugin('kafka'); const sendResp = kafka.send({ bootstrapServers: 'localhost:9092', topic: 'orders', key: 'order-1001', value: JSON.stringify({orderId: 1001, status: 'CREATED'}) }); pm.test('Kafka 发送成功', function () { pm.expect(sendResp.topic).to.equal('orders'); pm.expect(sendResp.offset).to.be.least(0); }); ``` ### pm.elasticsearch #### 方法列表 | 方法 | 参数 | 返回值 | 说明 | |------|------|--------|------| | `request(options)` | `baseUrl`、`method`、`path`、`body`、`headers` | Object | 执行一次 ES HTTP 请求 | | `query(options)` | 同 `request(options)` | Object | `request()` 的别名 | #### 示例:写入并断言 Elasticsearch ```javascript const indexResp = pm.elasticsearch.request({ baseUrl: 'http://localhost:9200', method: 'POST', path: '/orders/_doc/order-1001', body: JSON.stringify({orderId: 1001, status: 'CREATED'}) }); pm.test('ES 写入成功', function () { pm.expect(indexResp.code).to.be.within(200, 201); }); ``` ### pm.influxdb 支持两种模式: - `version: 'v1'` 或 `mode: 'influxql'`:使用 InfluxQL(默认走 `/query`、`/write`) - `version: 'v2'` 或 `mode: 'flux'`:使用 Flux / InfluxDB 2.x(走 `/api/v2/query`、`/api/v2/write`) #### 方法列表 | 方法 | 参数 | 返回值 | 说明 | |------|------|--------|------| | `query(options)` | `baseUrl`、`version`、`db/org`、`query`、`token` 等 | Object | 查询 InfluxDB | | `write(options)` | `baseUrl`、`version`、`db/org/bucket`、`lineProtocol`、`precision` | Object | 写入 line protocol 数据 | | `request(options)` | `baseUrl`、`path`、`method`、`body`、`headers` | Object | 发送原始 HTTP 请求 | #### 常用参数 | 参数 | 适用版本 | 说明 | |------|----------|------| | `baseUrl` | v1 / v2 | InfluxDB 地址,默认 `http://localhost:8086` | | `version` / `mode` | v1 / v2 | `v1` / `influxql` 或 `v2` / `flux` | | `db` / `database` | v1 | 数据库名 | | `org` | v2 | 组织名 | | `bucket` | v2 写入 | bucket 名 | | `token` | v2 | `Authorization: Token xxx` | | `username` / `password` | v1 | 通过 `u/p` 参数传递 | | `query` | v1 / v2 | InfluxQL 或 Flux 查询语句 | | `lineProtocol` | v1 / v2 写入 | 写入的 line protocol 文本 | | `precision` | v1 / v2 写入 | 时间精度,默认 `ms` | #### 示例:InfluxDB 1.x 查询并断言 ```javascript const queryResp = pm.influxdb.query({ baseUrl: 'http://localhost:8086', version: 'v1', db: 'metrics', username: 'root', password: 'root', query: 'SELECT * FROM cpu ORDER BY time DESC LIMIT 1' }); pm.test('InfluxQL 查询成功', function () { pm.expect(queryResp.code).to.equal(200); pm.expect(queryResp.json).to.exist(); }); ``` #### 示例:InfluxDB 1.x 写入并断言 ```javascript const writeResp = pm.influxdb.write({ baseUrl: 'http://localhost:8086', version: 'v1', db: 'metrics', username: 'root', password: 'root', precision: 'ms', lineProtocol: 'api_requests,service=order count=1i ' + Date.now() }); pm.test('Influx 写入成功', function () { pm.expect(writeResp.code).to.equal(204); }); ``` #### 示例:InfluxDB 2.x Flux 查询 ```javascript const resp = pm.influxdb.query({ baseUrl: 'http://localhost:8086', version: 'v2', org: 'demo-org', token: pm.environment.get('influxToken'), query: 'from(bucket: "metrics") |> range(start: -5m) |> limit(n: 1)' }); pm.test('Influx Flux 查询成功', function () { pm.expect(resp.code).to.equal(200); pm.expect(resp.body).to.include('_value'); }); ``` --- ## console - 控制台 输出调试信息。 ### 方法列表 | 方法 | 参数 | 说明 | 示例 | |-----------------------|--------------|------|-----------------------------------| | `log(message, ...)` | message: Any | 输出日志 | `console.log('Hello', 'World')` | | `info(message, ...)` | message: Any | 输出信息 | `console.info('Info message')` | | `warn(message, ...)` | message: Any | 输出警告 | `console.warn('Warning message')` | | `error(message, ...)` | message: Any | 输出错误 | `console.error('Error message')` | --- ## 内置 JavaScript 库 EasyPostman 内置了三个常用的 JavaScript 库,这些库**已预加载到全局作用域**,可以直接使用,无需 `require()`。 ### 支持的库列表 | 库名 | 全局变量名 | 版本 | 说明 | 官方文档 | |------------|-------------|----------|---------------------------------------|--------------------------------------------| | crypto-js | `CryptoJS` | 4.1.1 | 加密库,支持 AES、DES、MD5、SHA、HMAC 等多种加密算法 | [crypto-js](https://cryptojs.gitbook.io/docs/) | | lodash | `_` | 4.17.21 | JavaScript 实用工具库,提供丰富的函数式编程辅助方法 | [lodash](https://lodash.com/docs/) | | moment | `moment` | 2.29.4 | 日期时间处理库,用于格式化、解析和操作日期时间 | [moment.js](https://momentjs.com/docs/) | ### 使用方式 ```javascript // ✅ 推荐:直接使用全局变量(无需 require) var hash = CryptoJS.MD5('message').toString(); var randomNum = _.random(1, 100); var now = moment().format('YYYY-MM-DD HH:mm:ss'); // ✅ 也支持:使用 require() 加载 var CryptoJS = require('crypto-js'); var _ = require('lodash'); var moment = require('moment'); ``` ### `require()` 加载本地 / 网络脚本 除内置库外,脚本还支持通过 `require()` 加载你自己的 JavaScript 文件。 ```javascript // 1. 加载本地绝对路径脚本 var signer = require('/absolute/path/to/signer.js'); // 2. 加载相对路径脚本 // 顶层脚本中的 ./ 和 ../ 默认相对当前工作区目录解析 var helper = require('./scripts/helper.js'); // 3. 本地脚本内部继续引用相对依赖 // parent.js -> require('./child.js') var parent = require('/absolute/path/to/parent.js'); ``` 可以直接测试的现成示例: ```javascript // 仓库内置本地示例,local-parent.js 内部还会继续 require('./local-child.js') // 当前工作区指向仓库根目录时可直接执行 var localDemo = require('./docs/examples/js-require/local-parent.js'); console.log(localDemo.describe('easy-postman')); console.log(localDemo.marker()); ``` 自定义脚本建议使用 CommonJS 风格导出: ```javascript function sign(input) { return input + '-signed'; } module.exports = { sign }; ``` 网络脚本默认**关闭**,需要在“设置 -> 请求设置 -> 脚本加载”中显式开启“允许从网络加载脚本”后才允许加载。 ```javascript // 先到“设置 -> 请求设置 -> 脚本加载”开启远程脚本 var dayjs = require('https://cdn.jsdelivr.net/npm/dayjs@1.11.13/dayjs.min.js'); console.log(dayjs('2026-03-27').add(1, 'day').format('YYYY-MM-DD')); ``` 同一组设置里还可以控制: - 是否允许 `http://` 远程脚本(默认关闭,仅建议本地调试使用) - 远程脚本主机白名单 - 连接超时 / 读取超时 - 单个远程脚本最大大小 注意事项: - 顶层相对路径默认相对当前工作区目录解析;若当前工作区不可用,则退回到进程当前目录。 - 每次脚本执行结束后,`require()` 模块缓存都会清空,所以本地文件修改后下次执行会重新加载。 - 网络脚本本质上仍然是“下载后执行代码”,只建议加载你自己信任的脚本源。 ### crypto-js - 加密库 提供多种加密和哈希算法,常用于生成签名、加密敏感数据等场景。 #### 常用功能 | 功能 | 方法 | 说明 | 示例 | |-------------|-------------------------------------|---------------|------------------------------------------------------| | MD5 哈希 | `CryptoJS.MD5(message)` | 生成 MD5 哈希 | `CryptoJS.MD5('text').toString()` | | SHA1 哈希 | `CryptoJS.SHA1(message)` | 生成 SHA1 哈希 | `CryptoJS.SHA1('text').toString()` | | SHA256 哈希 | `CryptoJS.SHA256(message)` | 生成 SHA256 哈希 | `CryptoJS.SHA256('text').toString()` | | HMAC-SHA256 | `CryptoJS.HmacSHA256(message, key)` | HMAC 签名 | `CryptoJS.HmacSHA256('data', 'secret').toString()` | | AES 加密 | `CryptoJS.AES.encrypt(msg, key)` | AES 对称加密 | `CryptoJS.AES.encrypt('text', 'secret').toString()` | | AES 解密 | `CryptoJS.AES.decrypt(cipher, key)` | AES 解密 | `CryptoJS.AES.decrypt(cipher, 'secret').toString()` | | Base64 编码 | `CryptoJS.enc.Base64.stringify()` | Base64 编码 | `CryptoJS.enc.Base64.stringify(wordArray)` | | 随机字节 | `CryptoJS.lib.WordArray.random(n)` | 生成 n 字节随机数据 | `CryptoJS.lib.WordArray.random(16).toString()` | #### 使用示例 ```javascript // 直接使用全局变量 CryptoJS(无需 require) // MD5 哈希 var md5 = CryptoJS.MD5('password123').toString(); pm.environment.set('passwordHash', md5); // HMAC-SHA256 签名 var data = 'userId=123×tamp=' + Date.now(); var signature = CryptoJS.HmacSHA256(data, 'secret-key').toString(); pm.environment.set('signature', signature); // AES 加密解密 var encrypted = CryptoJS.AES.encrypt('sensitive data', 'my-key').toString(); var decrypted = CryptoJS.AES.decrypt(encrypted, 'my-key').toString(CryptoJS.enc.Utf8); ``` ### lodash - 工具库 提供数组、对象、字符串等数据类型的实用操作方法,简化数据处理逻辑。 #### 常用功能 | 分类 | 方法 | 说明 | 示例 | |------|-----------------------------|--------------|-----------------------------------------| | 数组 | `_.random(min, max)` | 生成随机数 | `_.random(1, 100)` | | | `_.shuffle(array)` | 打乱数组 | `_.shuffle([1, 2, 3])` | | | `_.sample(array)` | 随机取一个元素 | `_.sample(['a', 'b', 'c'])` | | | `_.uniq(array)` | 数组去重 | `_.uniq([1, 2, 2, 3])` | | 集合 | `_.map(collection, fn)` | 映射转换 | `_.map([1,2], n => n*2)` | | | `_.filter(collection, fn)` | 过滤 | `_.filter([1,2,3], n => n>1)` | | | `_.find(collection, fn)` | 查找第一个匹配项 | `_.find(users, {role: 'admin'})` | | | `_.groupBy(collection, fn)` | 分组 | `_.groupBy(users, 'role')` | | | `_.sortBy(collection, fn)` | 排序 | `_.sortBy(users, 'age')` | | 对象 | `_.pick(object, keys)` | 提取指定属性 | `_.pick(user, ['id', 'name'])` | | | `_.omit(object, keys)` | 排除指定属性 | `_.omit(user, ['password'])` | | | `_.merge(obj1, obj2)` | 深度合并对象 | `_.merge({a:1}, {b:2})` | | | `_.cloneDeep(object)` | 深度克隆 | `_.cloneDeep(complexObject)` | | 字符串 | `_.camelCase(string)` | 转驼峰命名 | `_.camelCase('hello-world')` | | | `_.snakeCase(string)` | 转蛇形命名 | `_.snakeCase('helloWorld')` | | | `_.capitalize(string)` | 首字母大写 | `_.capitalize('hello')` | | 其他 | `_.times(n, fn)` | 执行 n 次 | `_.times(3, i => console.log(i))` | | | `_.debounce(fn, wait)` | 防抖函数 | `_.debounce(func, 300)` | #### 使用示例 ```javascript // 直接使用全局变量 _(无需 require) // 生成随机测试数据 var userId = _.random(10000, 99999); var status = _.sample(['pending', 'approved', 'rejected']); // 数组操作 var nums = [1, 2, 3, 4, 5]; var doubled = _.map(nums, n => n * 2); var filtered = _.filter(nums, n => n > 2); // 对象操作 var user = {id: 1, name: 'John', password: 'secret', age: 30}; var safeUser = _.omit(user, ['password']); pm.environment.set('user', JSON.stringify(safeUser)); ``` ### moment - 日期时间库 强大的日期时间处理库,支持格式化、解析、计算和验证日期。 #### 常用功能 | 功能 | 方法 | 说明 | 示例 | |----------|-------------------------------------|----------------|---------------------------------------------| | 获取当前时间 | `moment()` | 创建当前时间对象 | `moment()` | | 格式化 | `moment().format(format)` | 格式化日期 | `moment().format('YYYY-MM-DD HH:mm:ss')` | | ISO格式 | `moment().toISOString()` | 转 ISO 8601 格式 | `moment().toISOString()` | | 时间戳 | `moment().valueOf()` | 获取毫秒时间戳 | `moment().valueOf()` | | | `moment().unix()` | 获取秒级时间戳 | `moment().unix()` | | 解析日期 | `moment(str, format)` | 解析字符串为日期 | `moment('2024-01-01', 'YYYY-MM-DD')` | | 日期加减 | `moment().add(n, unit)` | 增加时间 | `moment().add(7, 'days')` | | | `moment().subtract(n, unit)` | 减少时间 | `moment().subtract(1, 'months')` | | 日期比较 | `moment().isBefore(date)` | 是否在之前 | `moment().isBefore('2025-01-01')` | | | `moment().isAfter(date)` | 是否在之后 | `moment().isAfter('2023-01-01')` | | | `moment().isSame(date)` | 是否相同 | `moment().isSame('2024-01-01', 'day')` | | 时间差 | `moment().diff(date, unit)` | 计算时间差 | `moment().diff('2024-01-01', 'days')` | | 开始/结束时间 | `moment().startOf(unit)` | 获取单位开始时间 | `moment().startOf('day')` | | | `moment().endOf(unit)` | 获取单位结束时间 | `moment().endOf('month')` | | 验证 | `moment(str, format, true).isValid()` | 验证日期是否有效 | `moment('2024-13-01', 'YYYY-MM-DD', true).isValid()` | #### 使用示例 ```javascript // 直接使用全局变量 moment(无需 require) // 生成各种时间格式 pm.environment.set('currentDate', moment().format('YYYY-MM-DD')); pm.environment.set('currentTime', moment().format('YYYY-MM-DD HH:mm:ss')); pm.environment.set('timestamp', moment().valueOf().toString()); pm.environment.set('isoTime', moment().toISOString()); // 日期计算 var tomorrow = moment().add(1, 'days').format('YYYY-MM-DD'); var lastMonth = moment().subtract(1, 'months').format('YYYY-MM'); var startOfDay = moment().startOf('day').valueOf(); var endOfDay = moment().endOf('day').valueOf(); ``` ### 组合使用示例 ```javascript // 直接使用全局变量(无需 require) // CryptoJS、_(lodash)、moment 都已预加载 // 生成带签名的 API 请求参数 var params = { userId: pm.environment.get('userId') || '123', timestamp: moment().valueOf().toString(), nonce: CryptoJS.lib.WordArray.random(16).toString(CryptoJS.enc.Hex), action: 'getUserInfo' }; // 按键名排序并拼接签名字符串 var sortedKeys = _.keys(params).sort(); var signString = _.map(sortedKeys, key => key + '=' + params[key]).join('&'); // 生成 HMAC-SHA256 签名 var secretKey = pm.environment.get('secretKey') || 'default-secret'; var signature = CryptoJS.HmacSHA256(signString, secretKey).toString(); // 保存到环境变量 pm.environment.set('requestTimestamp', params.timestamp); pm.environment.set('requestNonce', params.nonce); pm.environment.set('requestSignature', signature); console.log('请求参数已准备完成'); console.log('签名字符串:', signString); console.log('签名:', signature); ``` --- ## 完整示例 ### Pre-request Script 示例 #### 示例 1:基础请求准备 ```javascript // 1. 设置环境变量 pm.environment.set('timestamp', Date.now()); pm.environment.set('requestId', pm.uuid()); // 2. 设置临时变量 pm.variables.set('localVar', 'tempValue'); // 3. 添加请求头 pm.request.headers.add({ key: 'X-Request-Time', value: new Date().toISOString() }); pm.request.headers.add({ key: 'X-Request-ID', value: pm.environment.get('requestId') }); // 4. 添加查询参数 pm.request.params.add({ key: 'timestamp', value: pm.getTimestamp().toString() }); // 5. 修改 URL 编码表单数据 pm.request.urlencoded.add({ key: 'username', value: 'john' }); // 6. 添加 multipart 表单数据 pm.request.formData.add({ key: 'userId', value: '123' }); // 7. 输出调试信息 console.log('Request URL:', pm.request.url.toString()); console.log('Request Method:', pm.request.method); console.log('Request ID:', pm.environment.get('requestId')); ``` #### 示例 2:JWT Token 认证 ```javascript // 检查 token 是否存在 const token = pm.environment.get('authToken'); if (token) { // 添加 Bearer Token pm.request.headers.upsert({ key: 'Authorization', value: 'Bearer ' + token }); console.log('已添加认证 Token'); } else { console.warn('警告:未找到认证 Token,请先登录'); } // 添加 API Key(如果需要) const apiKey = pm.environment.get('apiKey'); if (apiKey) { pm.request.headers.upsert({ key: 'X-API-Key', value: apiKey }); } ``` #### 示例 3:动态生成签名(HMAC-SHA256) ```javascript var CryptoJS = require('crypto-js'); // 获取请求信息 const timestamp = Date.now().toString(); const method = pm.request.method; const path = pm.request.url.getPath(); const secretKey = pm.environment.get('secretKey') || 'default-secret'; // 生成签名字符串 const signString = method + '\n' + path + '\n' + timestamp; console.log('签名字符串:', signString); // 计算 HMAC-SHA256 签名 const signature = CryptoJS.HmacSHA256(signString, secretKey).toString(); console.log('生成的签名:', signature); // 添加签名相关请求头 pm.request.headers.upsert({ key: 'X-Timestamp', value: timestamp }); pm.request.headers.upsert({ key: 'X-Signature', value: signature }); pm.request.headers.upsert({ key: 'X-App-Id', value: pm.environment.get('appId') || 'default-app' }); ``` #### 示例 4:动态数据生成器 ```javascript var moment = require('moment'); var _ = require('lodash'); // 生成随机用户数据 const randomUser = { id: pm.uuid(), username: 'user_' + _.random(10000, 99999), email: 'test_' + Date.now() + '@example.com', phone: '138' + _.random(10000000, 99999999), createTime: moment().format('YYYY-MM-DD HH:mm:ss'), age: _.random(18, 60) }; // 保存到环境变量供后续使用 pm.environment.set('testUserId', randomUser.id); pm.environment.set('testUserEmail', randomUser.email); pm.environment.set('testUsername', randomUser.username); // 如果是 JSON 请求体,可以动态修改 console.log('生成的测试用户:', JSON.stringify(randomUser, null, 2)); // 生成随机订单号 const orderId = 'ORD' + moment().format('YYYYMMDDHHmmss') + _.random(1000, 9999); pm.environment.set('testOrderId', orderId); console.log('订单号:', orderId); ``` #### 示例 5:条件请求修改 ```javascript // 根据环境变量决定请求配置 const env = pm.environment.get('currentEnv') || 'dev'; // 根据环境设置不同的 baseURL const baseUrls = { 'dev': 'https://dev-api.example.com', 'test': 'https://test-api.example.com', 'prod': 'https://api.example.com' }; pm.environment.set('baseUrl', baseUrls[env]); console.log('当前环境:', env, '- API 地址:', baseUrls[env]); // 根据请求方法添加不同的请求头 if (pm.request.method === 'POST' || pm.request.method === 'PUT') { pm.request.headers.upsert({ key: 'Content-Type', value: 'application/json' }); } // 为特定路径添加额外参数 if (pm.request.url.getPath().includes('/api/v2/')) { pm.request.params.add({ key: 'version', value: '2.0' }); } ``` #### 示例 6:请求数据校验 ```javascript // 检查必要的环境变量 const requiredVars = ['baseUrl', 'apiKey', 'userId']; const missingVars = []; requiredVars.forEach(function(varName) { if (!pm.environment.get(varName)) { missingVars.push(varName); } }); if (missingVars.length > 0) { console.error('错误:缺少必要的环境变量:', missingVars.join(', ')); throw new Error('缺少环境变量: ' + missingVars.join(', ')); } // 检查请求头是否完整 if (!pm.request.headers.has('Content-Type')) { console.warn('警告:缺少 Content-Type 请求头'); } console.log('✓ 环境变量校验通过'); ``` #### 示例 7:批量操作与数据处理 ```javascript var _ = require('lodash'); // 批量添加自定义请求头 const customHeaders = [ { key: 'X-Client-Version', value: '1.0.0' }, { key: 'X-Platform', value: 'web' }, { key: 'X-Device-ID', value: pm.environment.get('deviceId') || pm.uuid() }, { key: 'X-Session-ID', value: pm.environment.get('sessionId') || pm.uuid() } ]; customHeaders.forEach(function(header) { pm.request.headers.upsert(header); }); // 批量添加查询参数 const commonParams = { 'appId': pm.environment.get('appId') || 'default', 'lang': 'zh-CN', 'timezone': 'Asia/Shanghai', 'platform': 'web' }; _.forEach(commonParams, function(value, key) { pm.request.params.add({ key: key, value: value }); }); console.log('已添加', customHeaders.length, '个请求头'); console.log('已添加', Object.keys(commonParams).length, '个查询参数'); ``` #### 示例 8:URL 参数加密 ```javascript var CryptoJS = require('crypto-js'); // 获取敏感参数 const userId = pm.environment.get('userId'); const secretKey = pm.environment.get('encryptKey') || 'default-key'; if (userId) { // 加密用户 ID const encryptedUserId = CryptoJS.AES.encrypt(userId, secretKey).toString(); // 使用加密后的值 pm.request.params.add({ key: 'uid', value: encodeURIComponent(encryptedUserId) }); console.log('原始 userId:', userId); console.log('加密后:', encryptedUserId.substring(0, 20) + '...'); } // Base64 编码 const credentials = pm.environment.get('username') + ':' + pm.environment.get('password'); const base64Credentials = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(credentials)); pm.request.headers.upsert({ key: 'Authorization', value: 'Basic ' + base64Credentials }); ``` #### 示例 9:请求重试机制准备 ```javascript // 设置重试计数器 let retryCount = pm.environment.get('retryCount'); if (!retryCount) { retryCount = 0; } pm.environment.set('retryCount', retryCount); // 添加重试标识 pm.request.headers.upsert({ key: 'X-Retry-Count', value: retryCount.toString() }); // 设置超时时间(根据重试次数递增) const baseTimeout = 5000; const timeout = baseTimeout * (retryCount + 1); pm.environment.set('currentTimeout', timeout); console.log('重试次数:', retryCount, '- 超时时间:', timeout + 'ms'); ``` #### 示例 10:模拟数据填充(用于测试) ```javascript var _ = require('lodash'); var moment = require('moment'); // 生成模拟订单数据 const mockOrder = { orderId: 'TEST_' + moment().format('YYYYMMDDHHmmss') + _.random(1000, 9999), customerId: pm.environment.get('testUserId') || 'CUST_' + _.random(10000, 99999), products: _.times(_.random(1, 5), function(n) { return { productId: 'PROD_' + _.random(1000, 9999), quantity: _.random(1, 10), price: _.round(_.random(10, 1000, true), 2) }; }), totalAmount: 0, orderTime: moment().toISOString(), status: 'pending' }; // 计算总金额 mockOrder.totalAmount = _.sumBy(mockOrder.products, function(p) { return p.quantity * p.price; }); mockOrder.totalAmount = _.round(mockOrder.totalAmount, 2); // 保存模拟数据 pm.environment.set('mockOrderData', JSON.stringify(mockOrder)); console.log('生成的模拟订单:', JSON.stringify(mockOrder, null, 2)); ``` ### Post-request Script 示例 #### 示例 1:基础响应验证 ```javascript // 1. 状态码测试 pm.test("状态码是 200", function () { pm.response.to.have.status(200); }); // 2. 响应时间测试 pm.test("响应时间小于 500ms", function () { pm.expect(pm.response.responseTime).to.be.below(500); }); // 3. 响应头测试 pm.test("响应包含 Content-Type", function () { pm.response.to.have.header('Content-Type'); }); const contentType = pm.response.headers.get('Content-Type'); console.log('Content-Type:', contentType); // 4. JSON 结构测试 pm.test("响应包含正确的数据结构", function () { const jsonData = pm.response.json(); // 检查属性存在 pm.expect(jsonData).to.have.property('status'); pm.expect(jsonData).to.have.property('data'); // 检查值 pm.expect(jsonData.status).to.equal('success'); }); // 5. 字符串包含测试 pm.test("响应体包含 success", function () { pm.expect(pm.response.text()).to.include('success'); }); // 6. 正则匹配测试 pm.test("响应包含有效的 email", function () { const jsonData = pm.response.json(); pm.expect(jsonData.email).to.match(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/); }); // 7. 保存响应数据到环境变量 const responseData = pm.response.json(); if (responseData.token) { pm.environment.set('authToken', responseData.token); console.log('Token saved:', responseData.token); } if (responseData.userId) { pm.environment.set('userId', responseData.userId); } ``` #### 示例 2:登录接口完整测试 ```javascript pm.test("登录请求成功", function () { pm.response.to.have.status(200); }); pm.test("登录响应时间合理", function () { pm.expect(pm.response.responseTime).to.be.below(2000); }); const jsonData = pm.response.json(); pm.test("登录返回正确的数据结构", function () { pm.expect(jsonData).to.have.property('code'); pm.expect(jsonData).to.have.property('message'); pm.expect(jsonData).to.have.property('data'); pm.expect(jsonData.code).to.equal(200); }); pm.test("返回了认证令牌", function () { pm.expect(jsonData.data).to.have.property('token'); pm.expect(jsonData.data).to.have.property('refreshToken'); pm.expect(jsonData.data.token).to.match(/^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+$/); // JWT 格式 }); pm.test("返回了用户信息", function () { pm.expect(jsonData.data).to.have.property('userInfo'); pm.expect(jsonData.data.userInfo).to.have.property('userId'); pm.expect(jsonData.data.userInfo).to.have.property('username'); }); // 保存认证信息 if (jsonData.code === 200 && jsonData.data) { pm.environment.set('authToken', jsonData.data.token); pm.environment.set('refreshToken', jsonData.data.refreshToken); pm.environment.set('currentUserId', jsonData.data.userInfo.userId); pm.environment.set('currentUsername', jsonData.data.userInfo.username); console.log('✓ 登录成功,用户:', jsonData.data.userInfo.username); console.log('✓ Token 已保存'); } ``` #### 示例 3:数据列表接口测试 ```javascript const jsonData = pm.response.json(); pm.test("获取列表成功", function () { pm.response.to.have.status(200); pm.expect(jsonData.code).to.equal(200); }); pm.test("列表数据结构正确", function () { pm.expect(jsonData.data).to.have.property('list'); pm.expect(jsonData.data).to.have.property('total'); pm.expect(jsonData.data).to.have.property('pageNum'); pm.expect(jsonData.data).to.have.property('pageSize'); }); pm.test("列表数据有效", function () { pm.expect(Array.isArray(jsonData.data.list)).to.equal(true); pm.expect(jsonData.data.total).to.be.a('number'); pm.expect(jsonData.data.list.length).to.be.below(jsonData.data.pageSize + 1); }); // 验证列表项结构 if (jsonData.data.list.length > 0) { pm.test("列表项包含必要字段", function () { const firstItem = jsonData.data.list[0]; pm.expect(firstItem).to.have.property('id'); pm.expect(firstItem).to.have.property('name'); pm.expect(firstItem).to.have.property('createTime'); }); // 保存第一项的 ID 供后续测试使用 pm.environment.set('firstItemId', jsonData.data.list[0].id); console.log('总记录数:', jsonData.data.total); console.log('当前页记录数:', jsonData.data.list.length); } ``` #### 示例 4:错误处理和重试逻辑 ```javascript const statusCode = pm.response.code; const retryCount = parseInt(pm.environment.get('retryCount') || '0'); const maxRetries = 3; if (statusCode === 200) { // 成功,重置重试计数 pm.environment.set('retryCount', '0'); pm.test("请求成功", function () { pm.response.to.have.status(200); }); console.log('✓ 请求成功'); } else if (statusCode === 401) { // 未授权,可能 token 过期 pm.test("认证失败 - Token 可能已过期", function () { pm.expect(statusCode).to.equal(401); }); console.error('✗ 认证失败,请重新登录'); pm.environment.unset('authToken'); } else if (statusCode === 429) { // 请求过于频繁 pm.test("请求限流", function () { pm.expect(statusCode).to.equal(429); }); const retryAfter = pm.response.headers.get('Retry-After') || '60'; console.warn('⚠ 请求过于频繁,建议等待', retryAfter, '秒后重试'); } else if (statusCode >= 500) { // 服务器错误,可以重试 if (retryCount < maxRetries) { pm.environment.set('retryCount', (retryCount + 1).toString()); console.warn('⚠ 服务器错误,准备重试 (' + (retryCount + 1) + '/' + maxRetries + ')'); } else { console.error('✗ 达到最大重试次数,放弃重试'); pm.environment.set('retryCount', '0'); } } else { // 其他错误 pm.test("请求失败 - 状态码: " + statusCode, function () { const jsonData = pm.response.json(); console.error('错误信息:', jsonData.message || '未知错误'); }); } ``` #### 示例 5:Cookie 和 Session 管理 ```javascript // 8. Cookie 管理 pm.test("检查 session cookie", function () { pm.expect(pm.cookies.has('sessionId')).to.equal(true); }); const sessionCookie = pm.cookies.get('sessionId'); if (sessionCookie) { console.log('Session ID:', sessionCookie.value); pm.environment.set('sessionId', sessionCookie.value); } // 9. 从响应头获取 Cookie const responseCookie = pm.getResponseCookie('JSESSIONID'); if (responseCookie) { console.log('JSESSIONID:', responseCookie.value); pm.environment.set('jsessionId', responseCookie.value); } // 10. 使用 CookieJar 跨域设置 Cookie const jar = pm.cookies.jar(); jar.set('https://api.example.com', 'custom_token=xyz123', function (error, cookie) { if (error) { console.error('设置 cookie 失败:', error); } else { console.log('Cookie 设置成功'); } }); // 检查所有 Cookie console.log('=== 所有 Cookie ==='); const allCookies = pm.cookies.getAll(); allCookies.forEach(function(cookie) { console.log(cookie.name + ':', cookie.value); }); ``` #### 示例 6:性能监控和统计 ```javascript // 11. 获取响应大小信息 const size = pm.response.size(); console.log('响应体大小:', size.body, 'bytes'); console.log('响应头大小:', size.header, 'bytes'); console.log('总大小:', size.total, 'bytes'); // 性能统计 const responseTime = pm.response.responseTime; pm.environment.set('lastResponseTime', responseTime.toString()); // 计算平均响应时间 let totalTime = parseFloat(pm.environment.get('totalResponseTime') || '0'); let requestCount = parseInt(pm.environment.get('requestCount') || '0'); totalTime += responseTime; requestCount += 1; pm.environment.set('totalResponseTime', totalTime.toString()); pm.environment.set('requestCount', requestCount.toString()); const avgResponseTime = totalTime / requestCount; console.log('本次响应时间:', responseTime, 'ms'); console.log('平均响应时间:', avgResponseTime.toFixed(2), 'ms'); console.log('请求总数:', requestCount); // 性能等级判断 let performanceLevel = ''; if (responseTime < 100) { performanceLevel = '优秀'; } else if (responseTime < 300) { performanceLevel = '良好'; } else if (responseTime < 1000) { performanceLevel = '一般'; } else { performanceLevel = '较慢'; } pm.test("响应性能: " + performanceLevel + " (" + responseTime + "ms)", function () { console.log('性能等级:', performanceLevel); }); ``` #### 示例 7:复杂数据验证 ```javascript const jsonData = pm.response.json(); pm.test("验证嵌套数据结构", function () { // 多层嵌套验证 pm.expect(jsonData).to.have.property('data'); pm.expect(jsonData.data).to.have.property('user'); pm.expect(jsonData.data.user).to.have.property('profile'); pm.expect(jsonData.data.user.profile).to.have.property('address'); }); // 数组遍历验证 if (jsonData.data && jsonData.data.items) { pm.test("所有商品都有价格", function () { jsonData.data.items.forEach(function(item, index) { pm.expect(item).to.have.property('price'); pm.expect(item.price).to.be.a('number'); pm.expect(item.price).to.be.below(100000); console.log('商品' + (index + 1) + ':', item.name, '- 价格:', item.price); }); }); // 计算总价 let totalPrice = 0; jsonData.data.items.forEach(function(item) { totalPrice += item.price * item.quantity; }); pm.test("总价计算正确", function () { pm.expect(totalPrice).to.equal(jsonData.data.totalAmount); }); console.log('商品总价:', totalPrice); } ``` #### 示例 8:响应头详细分析 ```javascript // 12. 遍历所有响应头 console.log('所有响应头:'); pm.response.headers.each(function (header) { console.log(' ' + header.key + ': ' + header.value); }); // 验证安全相关响应头 pm.test("检查安全响应头", function () { const securityHeaders = [ 'X-Content-Type-Options', 'X-Frame-Options', 'X-XSS-Protection' ]; securityHeaders.forEach(function(headerName) { if (pm.response.headers.has(headerName)) { console.log('✓ 包含安全头:', headerName); } else { console.warn('⚠ 缺少安全头:', headerName); } }); }); // 检查缓存策略 if (pm.response.headers.has('Cache-Control')) { const cacheControl = pm.response.headers.get('Cache-Control'); console.log('缓存策略:', cacheControl); } // 检查 CORS 设置 if (pm.response.headers.has('Access-Control-Allow-Origin')) { const cors = pm.response.headers.get('Access-Control-Allow-Origin'); console.log('CORS 设置:', cors); } ``` #### 示例 10:数据提取和传递(API 链式调用) ```javascript // 从 JSON 响应中提取嵌套数据 pm.test("提取用户信息", function () { const jsonData = pm.response.json(); // 假设响应结构:{ data: { user: { id: 123, name: "John" } } } pm.expect(jsonData).to.have.property('data'); const userData = jsonData.data.user; pm.expect(userData).to.have.property('id'); pm.expect(userData).to.have.property('name'); // 保存到环境变量供下一个请求使用 pm.environment.set('currentUserId', userData.id.toString()); pm.environment.set('currentUserName', userData.name); // 如果有权限信息,也保存 if (userData.roles) { pm.environment.set('userRoles', JSON.stringify(userData.roles)); } }); // 处理数组响应 pm.test("处理数组数据", function () { const jsonData = pm.response.json(); // 假设响应是数组 pm.expect(Array.isArray(jsonData.items)).to.equal(true); // 检查第一个元素 if (jsonData.items.length > 0) { const firstItem = jsonData.items[0]; pm.expect(firstItem).to.have.property('id'); // 保存第一个项的 ID pm.environment.set('firstItemId', firstItem.id.toString()); } // 保存整个列表的 ID 数组 const itemIds = jsonData.items.map(function(item) { return item.id; }); pm.environment.set('allItemIds', JSON.stringify(itemIds)); console.log('提取了', itemIds.length, '个 ID'); }); ``` #### 示例 11:响应数据解密和验证 ```javascript var CryptoJS = require('crypto-js'); const jsonData = pm.response.json(); pm.test("响应包含加密数据", function () { pm.expect(jsonData).to.have.property('encryptedData'); }); // 解密响应数据 if (jsonData.encryptedData) { const secretKey = pm.environment.get('encryptKey') || 'default-key'; try { const decryptedBytes = CryptoJS.AES.decrypt(jsonData.encryptedData, secretKey); const decryptedText = decryptedBytes.toString(CryptoJS.enc.Utf8); const decryptedData = JSON.parse(decryptedText); console.log('✓ 数据解密成功'); console.log('解密后的数据:', decryptedData); // 验证解密后的数据 pm.test("解密后的数据有效", function () { pm.expect(decryptedData).to.have.property('userId'); pm.expect(decryptedData).to.have.property('balance'); }); // 保存解密后的数据 pm.environment.set('decryptedUserId', decryptedData.userId); pm.environment.set('userBalance', decryptedData.balance.toString()); } catch (error) { console.error('✗ 解密失败:', error.message); pm.test("数据解密失败", function () { throw new Error('解密失败: ' + error.message); }); } } ``` #### 示例 12:业务逻辑验证 ```javascript var _ = require('lodash'); var moment = require('moment'); const jsonData = pm.response.json(); // 订单状态验证 pm.test("订单状态有效", function () { const validStatuses = ['pending', 'processing', 'completed', 'cancelled']; pm.expect(validStatuses).to.include(jsonData.order.status); }); // 日期格式验证 pm.test("日期格式正确", function () { const createTime = jsonData.order.createTime; pm.expect(moment(createTime, moment.ISO_8601, true).isValid()).to.equal(true); }); // 金额计算验证 pm.test("订单金额计算正确", function () { const items = jsonData.order.items; let calculatedTotal = _.sumBy(items, function(item) { return item.price * item.quantity; }); // 加上运费 calculatedTotal += jsonData.order.shippingFee || 0; // 减去折扣 calculatedTotal -= jsonData.order.discount || 0; calculatedTotal = _.round(calculatedTotal, 2); pm.expect(calculatedTotal).to.equal(jsonData.order.totalAmount); console.log('计算金额:', calculatedTotal, '订单金额:', jsonData.order.totalAmount); }); // 库存验证 pm.test("商品库存充足", function () { jsonData.order.items.forEach(function(item) { pm.expect(item.quantity).to.be.below(item.stock + 1); if (item.quantity > item.stock * 0.8) { console.warn('⚠ 商品', item.name, '库存不足,剩余:', item.stock); } }); }); ``` ### 使用内置库示例 #### 示例 1:CryptoJS 加密库 ```javascript // 直接使用全局变量 CryptoJS(无需 require) // var CryptoJS = require('crypto-js'); // 可选,也支持这种方式 // 1. AES 加密/解密 const message = 'secret message'; const secretKey = 'my-secret-key-123'; // 加密 const encrypted = CryptoJS.AES.encrypt(message, secretKey).toString(); pm.environment.set('encrypted', encrypted); console.log('AES 加密:', encrypted); // 解密 const decrypted = CryptoJS.AES.decrypt(encrypted, secretKey); const decryptedText = decrypted.toString(CryptoJS.enc.Utf8); console.log('AES 解密:', decryptedText); // 2. MD5 哈希 const password = 'myPassword123'; const md5Hash = CryptoJS.MD5(password).toString(); console.log('MD5 哈希:', md5Hash); pm.environment.set('passwordHash', md5Hash); // 3. SHA256 哈希 const sha256Hash = CryptoJS.SHA256(password).toString(); console.log('SHA256 哈希:', sha256Hash); // 4. HMAC-SHA256 签名 const timestamp = Date.now().toString(); const data = 'userId=123×tamp=' + timestamp; const hmacKey = 'my-hmac-key'; const signature = CryptoJS.HmacSHA256(data, hmacKey).toString(); console.log('HMAC-SHA256 签名:', signature); // 5. Base64 编码/解码 const text = 'Hello World'; const base64Encoded = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(text)); console.log('Base64 编码:', base64Encoded); const base64Decoded = CryptoJS.enc.Base64.parse(base64Encoded).toString(CryptoJS.enc.Utf8); console.log('Base64 解码:', base64Decoded); // 6. 生成随机字符串 const randomBytes = CryptoJS.lib.WordArray.random(16); const randomString = randomBytes.toString(CryptoJS.enc.Hex); console.log('随机字符串:', randomString); pm.environment.set('nonce', randomString); ``` #### 示例 2:Lodash 数据处理库 ```javascript // 直接使用全局变量 _(无需 require) // var _ = require('lodash'); // 可选,也支持这种方式 // 1. 数组操作 const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // 过滤 const filtered = _.filter(numbers, function (n) { return n > 5; }); console.log('过滤结果:', filtered); // [6, 7, 8, 9, 10] // 映射 const doubled = _.map(numbers, function(n) { return n * 2; }); console.log('映射结果:', doubled); // 求和 const sum = _.sum(numbers); console.log('数组求和:', sum); // 取平均值 const avg = _.mean(numbers); console.log('平均值:', avg); // 2. 对象操作 const user = { id: 123, name: 'John Doe', email: 'john@example.com', age: 30, city: 'Beijing' }; // 提取属性值 const values = _.values(user); console.log('对象值:', values); // 提取属性名 const keys = _.keys(user); console.log('对象键:', keys); // 挑选特定属性 const picked = _.pick(user, ['id', 'name', 'email']); console.log('挑选属性:', picked); // 省略特定属性 const omitted = _.omit(user, ['age', 'city']); console.log('省略属性:', omitted); // 3. 集合操作 const users = [ { id: 1, name: 'Alice', age: 25, role: 'admin' }, { id: 2, name: 'Bob', age: 30, role: 'user' }, { id: 3, name: 'Charlie', age: 35, role: 'user' }, { id: 4, name: 'David', age: 28, role: 'admin' } ]; // 查找 const admin = _.find(users, { role: 'admin' }); console.log('第一个管理员:', admin); // 过滤 const admins = _.filter(users, { role: 'admin' }); console.log('所有管理员:', admins); // 分组 const grouped = _.groupBy(users, 'role'); console.log('按角色分组:', grouped); // 排序 const sorted = _.sortBy(users, ['age']); console.log('按年龄排序:', sorted); // 提取特定字段 const names = _.map(users, 'name'); console.log('所有姓名:', names); // 统计 const sumAges = _.sumBy(users, 'age'); console.log('年龄总和:', sumAges); // 4. 字符串操作 const str = 'hello world'; const capitalized = _.capitalize(str); console.log('首字母大写:', capitalized); const camelCase = _.camelCase('hello-world-example'); console.log('驼峰命名:', camelCase); // helloWorldExample const snakeCase = _.snakeCase('helloWorldExample'); console.log('蛇形命名:', snakeCase); // hello_world_example // 5. 随机数生成 const randomNum = _.random(1, 100); console.log('随机整数:', randomNum); const randomFloat = _.random(1.5, 5.5, true); console.log('随机浮点数:', randomFloat); // 6. 去重 const duplicates = [1, 2, 2, 3, 4, 4, 5]; const unique = _.uniq(duplicates); console.log('去重结果:', unique); // 7. 数组操作 const arr1 = [1, 2, 3]; const arr2 = [3, 4, 5]; // 交集 const intersection = _.intersection(arr1, arr2); console.log('交集:', intersection); // [3] // 并集 const union = _.union(arr1, arr2); console.log('并集:', union); // [1, 2, 3, 4, 5] // 差集 const difference = _.difference(arr1, arr2); console.log('差集:', difference); // [1, 2] // 8. 深度克隆 const original = { a: 1, b: { c: 2 } }; const cloned = _.cloneDeep(original); cloned.b.c = 999; console.log('原始对象:', original.b.c); // 2 console.log('克隆对象:', cloned.b.c); // 999 // 9. 合并对象 const obj1 = { a: 1, b: 2 }; const obj2 = { b: 3, c: 4 }; const merged = _.merge({}, obj1, obj2); console.log('合并结果:', merged); // { a: 1, b: 3, c: 4 } // 10. 防抖和节流(在循环中使用) const processData = _.debounce(function(data) { console.log('处理数据:', data); }, 300); // 11. 链式调用 const result = _(numbers) .filter(function(n) { return n % 2 === 0; }) .map(function(n) { return n * n; }) .sum(); console.log('偶数平方和:', result); ``` #### 示例 3:Moment.js 日期时间库 ```javascript // 直接使用全局变量 moment(无需 require) // var moment = require('moment'); // 可选,也支持这种方式 // 1. 获取当前时间 const now = moment(); console.log('当前时间:', now.format('YYYY-MM-DD HH:mm:ss')); // 2. 格式化日期 const formatted = moment().format('YYYY-MM-DD HH:mm:ss'); pm.environment.set('currentTime', formatted); console.log('格式化时间:', formatted); // ISO 8601 格式 const iso = moment().toISOString(); console.log('ISO 格式:', iso); // Unix 时间戳 const timestamp = moment().unix(); console.log('Unix 时间戳:', timestamp); // 毫秒时间戳 const milliseconds = moment().valueOf(); console.log('毫秒时间戳:', milliseconds); // 3. 解析日期 const parsed1 = moment('2024-01-01', 'YYYY-MM-DD'); console.log('解析日期:', parsed1.format('YYYY年MM月DD日')); const parsed2 = moment('01/15/2024', 'MM/DD/YYYY'); console.log('美式日期:', parsed2.format('YYYY-MM-DD')); // 从时间戳解析 const fromTimestamp = moment(1704067200000); console.log('时间戳解析:', fromTimestamp.format('YYYY-MM-DD HH:mm:ss')); // 4. 日期计算 const tomorrow = moment().add(1, 'days'); console.log('明天:', tomorrow.format('YYYY-MM-DD')); const nextWeek = moment().add(1, 'weeks'); console.log('下周:', nextWeek.format('YYYY-MM-DD')); const nextMonth = moment().add(1, 'months'); console.log('下月:', nextMonth.format('YYYY-MM-DD')); const yesterday = moment().subtract(1, 'days'); console.log('昨天:', yesterday.format('YYYY-MM-DD')); // 5. 日期比较 const date1 = moment('2024-01-01'); const date2 = moment('2024-12-31'); console.log('date1 在 date2 之前:', date1.isBefore(date2)); // true console.log('date1 在 date2 之后:', date1.isAfter(date2)); // false console.log('日期相同:', date1.isSame(date2)); // false // 6. 日期差异 const start = moment('2024-01-01'); const end = moment('2024-12-31'); const diffDays = end.diff(start, 'days'); console.log('相差天数:', diffDays); const diffMonths = end.diff(start, 'months'); console.log('相差月数:', diffMonths); const diffYears = end.diff(start, 'years'); console.log('相差年数:', diffYears); // 7. 开始和结束时间 const startOfDay = moment().startOf('day'); console.log('今天开始:', startOfDay.format('YYYY-MM-DD HH:mm:ss')); const endOfDay = moment().endOf('day'); console.log('今天结束:', endOfDay.format('YYYY-MM-DD HH:mm:ss')); const startOfMonth = moment().startOf('month'); console.log('本月开始:', startOfMonth.format('YYYY-MM-DD')); const endOfMonth = moment().endOf('month'); console.log('本月结束:', endOfMonth.format('YYYY-MM-DD')); // 8. 相对时间 const aWeekAgo = moment().subtract(7, 'days'); console.log('一周前:', aWeekAgo.fromNow()); // 7 days ago const inThreeDays = moment().add(3, 'days'); console.log('三天后:', inThreeDays.fromNow()); // in 3 days // 9. 验证日期 const validDate = moment('2024-01-01', 'YYYY-MM-DD', true).isValid(); console.log('日期有效:', validDate); // true const invalidDate = moment('2024-13-01', 'YYYY-MM-DD', true).isValid(); console.log('日期无效:', invalidDate); // false // 10. 实用场景:生成各种时间格式 pm.environment.set('dateYMD', moment().format('YYYY-MM-DD')); pm.environment.set('dateYMDHMS', moment().format('YYYY-MM-DD HH:mm:ss')); pm.environment.set('dateISO', moment().toISOString()); pm.environment.set('timestamp', moment().valueOf().toString()); pm.environment.set('dateChina', moment().format('YYYY年MM月DD日 HH时mm分ss秒')); // 11. 时区处理(如果支持) const utc = moment.utc(); console.log('UTC 时间:', utc.format('YYYY-MM-DD HH:mm:ss')); // 12. 业务场景:生成时间范围 const today = moment().startOf('day'); const todayEnd = moment().endOf('day'); pm.environment.set('queryStartTime', today.valueOf().toString()); pm.environment.set('queryEndTime', todayEnd.valueOf().toString()); console.log('查询开始时间:', today.format('YYYY-MM-DD HH:mm:ss')); console.log('查询结束时间:', todayEnd.format('YYYY-MM-DD HH:mm:ss')); ``` #### 示例 4:组合使用多个库 ```javascript var _ = require('lodash'); var moment = require('moment'); var CryptoJS = require('crypto-js'); // 场景:生成带签名的 API 请求 // 1. 准备请求参数 const params = { userId: pm.environment.get('userId') || '123', timestamp: moment().valueOf().toString(), nonce: CryptoJS.lib.WordArray.random(16).toString(CryptoJS.enc.Hex), action: 'getUserInfo' }; // 2. 按键名排序参数 const sortedKeys = _.keys(params).sort(); console.log('排序后的键:', sortedKeys); // 3. 拼接签名字符串 const signString = _.map(sortedKeys, function(key) { return key + '=' + params[key]; }).join('&'); console.log('签名字符串:', signString); // 4. 生成签名 const secretKey = pm.environment.get('secretKey') || 'default-secret'; const signature = CryptoJS.HmacSHA256(signString, secretKey).toString(); console.log('签名:', signature); // 5. 保存到环境变量 pm.environment.set('requestTimestamp', params.timestamp); pm.environment.set('requestNonce', params.nonce); pm.environment.set('requestSignature', signature); // 6. 打印完整请求参数 const fullParams = _.assign({}, params, { signature: signature }); console.log('完整请求参数:', JSON.stringify(fullParams, null, 2)); ``` #### 示例 5:数据模拟生成器 ```javascript var _ = require('lodash'); var moment = require('moment'); // 生成批量测试数据 const mockUsers = _.times(10, function(index) { return { id: 1000 + index, username: 'user_' + _.random(10000, 99999), email: 'test' + index + '@example.com', age: _.random(18, 60), gender: _.sample(['male', 'female']), city: _.sample(['Beijing', 'Shanghai', 'Guangzhou', 'Shenzhen']), registerTime: moment().subtract(_.random(1, 365), 'days').format('YYYY-MM-DD HH:mm:ss'), lastLoginTime: moment().subtract(_.random(0, 30), 'days').format('YYYY-MM-DD HH:mm:ss'), isActive: _.sample([true, false]), score: _.round(_.random(0, 100, true), 2) }; }); console.log('生成了', mockUsers.length, '个模拟用户'); console.log('示例用户:', JSON.stringify(mockUsers[0], null, 2)); // 保存第一个用户信息 pm.environment.set('testUserId', mockUsers[0].id.toString()); pm.environment.set('testUsername', mockUsers[0].username); pm.environment.set('mockUsersData', JSON.stringify(mockUsers)); // 统计信息 const avgAge = _.meanBy(mockUsers, 'age'); const avgScore = _.meanBy(mockUsers, 'score'); const activeCount = _.filter(mockUsers, { isActive: true }).length; console.log('平均年龄:', _.round(avgAge, 1)); console.log('平均得分:', _.round(avgScore, 2)); console.log('活跃用户数:', activeCount); ``` --- ## 高级代码片段示例 ### 代码片段 1:OAuth 2.0 Token 自动刷新 ```javascript // Pre-request Script: 自动检查和刷新 Token const tokenExpireTime = pm.environment.get('tokenExpireTime'); const currentTime = Date.now(); // 检查 token 是否过期(提前5分钟刷新) if (!tokenExpireTime || currentTime > (parseInt(tokenExpireTime) - 300000)) { console.log('Token 即将过期或已过期,需要刷新'); pm.sendRequest({ url: pm.environment.get('authUrl'), method: 'POST', header: { 'Content-Type': 'application/json' }, body: { mode: 'raw', raw: JSON.stringify({ refreshToken: pm.environment.get('refreshToken') }) } }, function (err, response) { if (err) { console.error('刷新 token 失败:', err.message); return; } const data = response.json(); const token = data.accessToken || data.token; if (token) { pm.environment.set('authToken', token); pm.request.headers.upsert({ key: 'Authorization', value: 'Bearer ' + token }); console.log('✓ Token 刷新成功,已更新认证头'); } }); } else { const token = pm.environment.get('authToken'); if (token) { pm.request.headers.upsert({ key: 'Authorization', value: 'Bearer ' + token }); console.log('✓ Token 有效,已添加认证头'); } } ``` ### 代码片段 2:接口限流处理(本地计数) ```javascript // Pre-request Script: 本地请求限流检查 const lastRequestTime = pm.environment.get('lastRequestTime'); const minInterval = 100; // 最小请求间隔(毫秒) if (lastRequestTime) { const timeSinceLastRequest = Date.now() - parseInt(lastRequestTime); if (timeSinceLastRequest < minInterval) { const waitTime = minInterval - timeSinceLastRequest; console.warn('⚠ 请求过于频繁,建议等待', waitTime, 'ms'); } } pm.environment.set('lastRequestTime', Date.now().toString()); ``` ### 代码片段 3:动态环境切换 ```javascript // Pre-request Script: 根据变量切换环境配置 const env = pm.environment.get('ENV') || 'dev'; const configs = { dev: { baseUrl: 'https://dev-api.example.com', timeout: 10000, debug: true }, test: { baseUrl: 'https://test-api.example.com', timeout: 8000, debug: true }, staging: { baseUrl: 'https://staging-api.example.com', timeout: 5000, debug: false }, prod: { baseUrl: 'https://api.example.com', timeout: 5000, debug: false } }; const config = configs[env]; if (!config) { throw new Error('未知环境: ' + env); } // 应用配置 pm.environment.set('baseUrl', config.baseUrl); pm.environment.set('timeout', config.timeout.toString()); pm.environment.set('debug', config.debug.toString()); // 添加环境标识头 pm.request.headers.upsert({ key: 'X-Environment', value: env }); console.log('✓ 已切换到', env, '环境'); console.log(' Base URL:', config.baseUrl); console.log(' Timeout:', config.timeout); ``` ### 代码片段 4:请求体动态模板填充 ```javascript // Pre-request Script: JSON 模板动态替换 var _ = require('lodash'); var moment = require('moment'); // 定义请求体模板(可以从环境变量获取) const requestTemplate = { "requestId": "{{requestId}}", "timestamp": "{{timestamp}}", "userId": "{{userId}}", "action": "{{action}}", "data": { "startDate": "{{startDate}}", "endDate": "{{endDate}}", "pageNum": "{{pageNum}}", "pageSize": "{{pageSize}}" } }; // 准备替换数据 const templateData = { requestId: pm.uuid(), timestamp: moment().valueOf(), userId: pm.environment.get('userId') || '123', action: 'queryOrders', startDate: moment().subtract(7, 'days').format('YYYY-MM-DD'), endDate: moment().format('YYYY-MM-DD'), pageNum: 1, pageSize: 20 }; // 替换模板 let requestBodyStr = JSON.stringify(requestTemplate); _.forEach(templateData, function(value, key) { const placeholder = '{{' + key + '}}'; requestBodyStr = requestBodyStr.replace(new RegExp(placeholder, 'g'), value); }); const requestBody = JSON.parse(requestBodyStr); console.log('生成的请求体:', JSON.stringify(requestBody, null, 2)); // 保存到环境变量供请求使用 pm.environment.set('dynamicRequestBody', JSON.stringify(requestBody)); ``` ### 代码片段 5:批量参数验证器 ```javascript // Pre-request Script: 请求参数验证 const validationRules = { userId: { required: true, type: 'string', pattern: /^[0-9]+$/, message: 'userId 必须是数字字符串' }, email: { required: false, type: 'string', pattern: /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/, message: 'email 格式不正确' }, age: { required: false, type: 'number', min: 0, max: 150, message: 'age 必须在 0-150 之间' } }; const params = { userId: pm.environment.get('userId'), email: pm.environment.get('email'), age: parseInt(pm.environment.get('age')) }; const errors = []; // 执行验证 Object.keys(validationRules).forEach(function(key) { const rule = validationRules[key]; const value = params[key]; // 必填校验 if (rule.required && (!value || value === '')) { errors.push(key + ' 是必填项'); return; } if (value !== undefined && value !== null && value !== '') { // 类型校验 if (rule.type === 'number' && typeof value !== 'number') { errors.push(key + ' 必须是数字类型'); } // 正则校验 if (rule.pattern && !rule.pattern.test(String(value))) { errors.push(rule.message || key + ' 格式不正确'); } // 范围校验 if (rule.type === 'number') { if (rule.min !== undefined && value < rule.min) { errors.push(key + ' 不能小于 ' + rule.min); } if (rule.max !== undefined && value > rule.max) { errors.push(key + ' 不能大于 ' + rule.max); } } } }); if (errors.length > 0) { console.error('参数验证失败:'); errors.forEach(function(error) { console.error(' ✗', error); }); throw new Error('参数验证失败: ' + errors.join('; ')); } else { console.log('✓ 所有参数验证通过'); } ``` ### 代码片段 6:响应数据提取链(链式调用场景) ```javascript // Post-request Script: 从复杂嵌套响应中提取数据 var _ = require('lodash'); const jsonData = pm.response.json(); pm.test("响应结构正确", function () { pm.response.to.have.status(200); pm.expect(jsonData.code).to.equal(0); }); // 使用 lodash 从复杂结构提取数据 const extractedData = { // 提取用户 ID 列表 userIds: _.map(_.get(jsonData, 'data.users', []), 'id'), // 提取第一个用户的详细信息 firstUser: _.get(jsonData, 'data.users[0]', null), // 提取所有管理员 admins: _.filter(_.get(jsonData, 'data.users', []), { role: 'admin' }), // 计算总金额 totalAmount: _.sumBy(_.get(jsonData, 'data.orders', []), 'amount'), // 按状态分组订单 ordersByStatus: _.groupBy(_.get(jsonData, 'data.orders', []), 'status'), // 提取分页信息 pagination: _.pick(_.get(jsonData, 'data', {}), ['pageNum', 'pageSize', 'total']) }; console.log('提取的数据:', JSON.stringify(extractedData, null, 2)); // 保存关键信息到环境变量 if (extractedData.firstUser) { pm.environment.set('firstUserId', extractedData.firstUser.id); pm.environment.set('firstUserName', extractedData.firstUser.name); } if (extractedData.userIds.length > 0) { pm.environment.set('userIdList', JSON.stringify(extractedData.userIds)); } pm.environment.set('totalAmount', extractedData.totalAmount.toString()); console.log('✓ 提取了', extractedData.userIds.length, '个用户ID'); console.log('✓ 总金额:', extractedData.totalAmount); ``` ### 代码片段 7:性能基准测试 ```javascript // Post-request Script: 性能监控和基准对比 var _ = require('lodash'); const responseTime = pm.response.responseTime; const endpoint = pm.request.url.getPath(); // 定义性能基准(毫秒) const benchmarks = { '/api/user/login': 500, '/api/user/profile': 300, '/api/orders/list': 1000, '/api/products/search': 800, 'default': 1000 }; const benchmark = benchmarks[endpoint] || benchmarks['default']; // 性能等级评估 let performanceGrade = ''; let performanceScore = 100; if (responseTime <= benchmark * 0.3) { performanceGrade = 'A+ (优秀)'; performanceScore = 100; } else if (responseTime <= benchmark * 0.5) { performanceGrade = 'A (良好)'; performanceScore = 90; } else if (responseTime <= benchmark * 0.8) { performanceGrade = 'B (一般)'; performanceScore = 75; } else if (responseTime <= benchmark) { performanceGrade = 'C (及格)'; performanceScore = 60; } else if (responseTime <= benchmark * 1.5) { performanceGrade = 'D (较慢)'; performanceScore = 40; } else { performanceGrade = 'F (缓慢)'; performanceScore = 20; } // 记录性能数据 const perfKey = 'perf_' + endpoint.replace(/\//g, '_'); let perfHistory = pm.environment.get(perfKey); if (!perfHistory) { perfHistory = []; } else { try { perfHistory = JSON.parse(perfHistory); } catch (e) { perfHistory = []; } } perfHistory.push(responseTime); // 只保留最近10次记录 if (perfHistory.length > 10) { perfHistory = _.takeRight(perfHistory, 10); } pm.environment.set(perfKey, JSON.stringify(perfHistory)); // 计算平均响应时间 const avgResponseTime = _.mean(perfHistory); const minResponseTime = _.min(perfHistory); const maxResponseTime = _.max(perfHistory); // 输出性能报告 console.log('=== 性能测试报告 ==='); console.log('接口:', endpoint); console.log('本次响应时间:', responseTime, 'ms'); console.log('性能等级:', performanceGrade, '(得分:', performanceScore + ')'); console.log('性能基准:', benchmark, 'ms'); console.log('历史平均:', _.round(avgResponseTime, 2), 'ms'); console.log('最快响应:', minResponseTime, 'ms'); console.log('最慢响应:', maxResponseTime, 'ms'); // 创建性能测试 pm.test("性能测试: " + performanceGrade, function () { pm.expect(responseTime).to.be.below(benchmark); }); // 如果性能下降,发出警告 if (responseTime > avgResponseTime * 1.5) { console.warn('⚠️ 警告: 本次响应时间比平均值慢了', _.round((responseTime / avgResponseTime - 1) * 100, 1) + '%'); } ``` ### 代码片段 8:数据一致性校验 ```javascript // Post-request Script: 复杂业务逻辑验证 var _ = require('lodash'); var moment = require('moment'); const jsonData = pm.response.json(); pm.test("订单数据一致性校验", function () { if (!jsonData.data || !jsonData.data.order) { throw new Error('订单数据不存在'); } const order = jsonData.data.order; // 1. 金额计算验证 const calculatedSubtotal = _.sumBy(order.items, function(item) { return item.price * item.quantity; }); pm.expect(_.round(calculatedSubtotal, 2)).to.equal(order.subtotal); console.log('✓ 小计金额正确:', order.subtotal); // 2. 总金额验证(小计 + 运费 - 折扣) const calculatedTotal = _.round( order.subtotal + (order.shippingFee || 0) - (order.discount || 0), 2 ); pm.expect(calculatedTotal).to.equal(order.totalAmount); console.log('✓ 总金额正确:', order.totalAmount); // 3. 状态转换逻辑验证 const validStatusFlow = { 'pending': ['processing', 'cancelled'], 'processing': ['shipped', 'cancelled'], 'shipped': ['delivered', 'return_requested'], 'delivered': ['completed', 'return_requested'], 'return_requested': ['refunding', 'delivered'], 'refunding': ['refunded'], 'completed': [], 'cancelled': [], 'refunded': [] }; const currentStatus = order.status; console.log('✓ 当前订单状态:', currentStatus); // 4. 日期逻辑验证 if (order.createTime && order.updateTime) { const createTime = moment(order.createTime); const updateTime = moment(order.updateTime); pm.expect(updateTime.isSameOrAfter(createTime)).to.equal(true); console.log('✓ 更新时间不早于创建时间'); } // 5. 商品数量验证 pm.expect(order.items.length).to.be.above(0); order.items.forEach(function(item, index) { // 数量必须大于0 pm.expect(item.quantity).to.be.above(0); // 价格必须大于等于0 pm.expect(item.price).to.be.below(999999); // SKU 不能为空 pm.expect(item.sku).to.be.a('string'); pm.expect(item.sku.length).to.be.above(0); console.log('✓ 商品', (index + 1), '验证通过:', item.name); }); }); // 6. 数据完整性检查 pm.test("必填字段检查", function () { const requiredFields = [ 'orderId', 'userId', 'status', 'totalAmount', 'createTime', 'items' ]; const order = jsonData.data.order; requiredFields.forEach(function(field) { pm.expect(order).to.have.property(field); pm.expect(order[field]).to.not.equal(null); pm.expect(order[field]).to.not.equal(undefined); console.log('✓ 字段', field, '存在且有值'); }); }); ``` ### 代码片段 9:自定义断言函数库 ```javascript // Post-request Script: 创建可复用的断言函数 // 定义自定义断言函数库 const customAssert = { // 断言数组包含特定元素 arrayContains: function(array, element, message) { const contains = array.indexOf(element) !== -1; if (!contains) { throw new Error(message || '数组不包含元素: ' + element); } return true; }, // 断言字符串长度在范围内 stringLengthBetween: function(str, min, max, message) { if (str.length < min || str.length > max) { throw new Error(message || '字符串长度应在 ' + min + '-' + max + ' 之间'); } return true; }, // 断言日期在范围内 dateInRange: function(dateStr, startDate, endDate, message) { const date = new Date(dateStr); const start = new Date(startDate); const end = new Date(endDate); if (date < start || date > end) { throw new Error(message || '日期不在指定范围内'); } return true; }, // 断言对象结构匹配 objectMatchesSchema: function(obj, schema, message) { for (let key in schema) { if (!obj.hasOwnProperty(key)) { throw new Error(message || '对象缺少字段: ' + key); } const expectedType = schema[key]; const actualType = typeof obj[key]; if (actualType !== expectedType && !(expectedType === 'array' && Array.isArray(obj[key]))) { throw new Error(message || '字段 ' + key + ' 类型错误,期望: ' + expectedType + ',实际: ' + actualType); } } return true; }, // 断言枚举值 isOneOf: function(value, allowedValues, message) { if (allowedValues.indexOf(value) === -1) { throw new Error(message || '值必须是以下之一: ' + allowedValues.join(', ')); } return true; }, // 断言数值范围 numberInRange: function(num, min, max, message) { if (num < min || num > max) { throw new Error(message || '数值应在 ' + min + '-' + max + ' 之间'); } return true; } }; // 使用自定义断言 const jsonData = pm.response.json(); pm.test("自定义断言测试", function () { const user = jsonData.data.user; // 测试对象结构 customAssert.objectMatchesSchema(user, { id: 'string', name: 'string', age: 'number', email: 'string', roles: 'array' }); console.log('✓ 对象结构匹配'); // 测试枚举值 customAssert.isOneOf(user.status, ['active', 'inactive', 'suspended']); console.log('✓ 状态值有效'); // 测试数值范围 customAssert.numberInRange(user.age, 0, 150); console.log('✓ 年龄在有效范围内'); // 测试字符串长度 customAssert.stringLengthBetween(user.name, 1, 50); console.log('✓ 名称长度有效'); // 测试数组包含 customAssert.arrayContains(user.roles, 'user'); console.log('✓ 角色列表包含基础角色'); }); ``` ### 代码片段 10:多语言国际化测试 ```javascript // Pre-request Script: 国际化多语言测试 const languages = ['zh-CN', 'en-US', 'ja-JP', 'ko-KR', 'fr-FR']; const currentLang = pm.environment.get('testLanguage') || 'zh-CN'; // 设置语言请求头 pm.request.headers.upsert({ key: 'Accept-Language', value: currentLang }); pm.request.headers.upsert({ key: 'X-Locale', value: currentLang }); console.log('当前测试语言:', currentLang); // Post-request Script: 验证多语言响应 pm.test("多语言响应验证", function () { const jsonData = pm.response.json(); const lang = pm.request.headers.get('Accept-Language') || 'zh-CN'; // 检查响应中的语言字段 if (jsonData.message) { console.log('响应消息 (' + lang + '):', jsonData.message); // 验证中文响应包含中文字符 if (lang === 'zh-CN') { pm.expect(jsonData.message).to.match(/[\u4e00-\u9fa5]/); } // 验证英文响应只包含ASCII字符 if (lang === 'en-US') { pm.expect(jsonData.message).to.match(/^[\x00-\x7F]*$/); } } }); ``` ### 代码片段 11:幂等性测试 ```javascript // Post-request Script: 幂等性验证(需要多次执行同一请求) const requestId = pm.environment.get('idempotencyTestId'); const responseBody = pm.response.text(); const responseHash = CryptoJS.MD5(responseBody).toString(); if (!requestId) { // 第一次请求,保存请求ID和响应哈希 pm.environment.set('idempotencyTestId', pm.uuid()); pm.environment.set('idempotencyResponseHash', responseHash); pm.environment.set('idempotencyCount', '1'); console.log('幂等性测试:第1次请求,已保存响应基准'); } else { // 后续请求,验证响应是否一致 const savedHash = pm.environment.get('idempotencyResponseHash'); let count = parseInt(pm.environment.get('idempotencyCount') || '1'); count++; pm.environment.set('idempotencyCount', count.toString()); pm.test("幂等性验证 - 第" + count + "次请求", function () { pm.expect(responseHash).to.equal(savedHash); console.log('✓ 响应与第1次请求一致'); }); console.log('幂等性测试:已执行', count, '次请求'); // 完成5次测试后清理 if (count >= 5) { pm.environment.unset('idempotencyTestId'); pm.environment.unset('idempotencyResponseHash'); pm.environment.unset('idempotencyCount'); console.log('✓ 幂等性测试完成,已清理测试数据'); } } ``` ### 代码片段 12:响应时间统计图表数据生成 ```javascript // Post-request Script: 收集性能数据用于图表展示 var moment = require('moment'); var _ = require('lodash'); const endpoint = pm.request.url.getPath(); const responseTime = pm.response.responseTime; const timestamp = moment().format('YYYY-MM-DD HH:mm:ss'); // 获取历史数据 let perfData = pm.environment.get('performanceChartData'); if (!perfData) { perfData = []; } else { try { perfData = JSON.parse(perfData); } catch (e) { perfData = []; } } // 添加新数据点 perfData.push({ endpoint: endpoint, responseTime: responseTime, timestamp: timestamp, statusCode: pm.response.code, success: pm.response.code === 200 }); // 保留最近100条记录 if (perfData.length > 100) { perfData = _.takeRight(perfData, 100); } pm.environment.set('performanceChartData', JSON.stringify(perfData)); // 按接口分组统计 const groupedByEndpoint = _.groupBy(perfData, 'endpoint'); const stats = {}; _.forEach(groupedByEndpoint, function(records, endpoint) { const times = _.map(records, 'responseTime'); stats[endpoint] = { count: records.length, avg: _.round(_.mean(times), 2), min: _.min(times), max: _.max(times), p50: _.round(times.sort()[Math.floor(times.length * 0.5)], 2), p95: _.round(times.sort()[Math.floor(times.length * 0.95)], 2), successRate: _.round(_.filter(records, { success: true }).length / records.length * 100, 2) }; }); console.log('=== 性能统计数据 ==='); console.log(JSON.stringify(stats, null, 2)); // 保存统计数据 pm.environment.set('performanceStats', JSON.stringify(stats)); ``` --- ## 注意事项 1. **作用域限制** - Pre-request 脚本中无法访问 `pm.response` - `pm.response` 仅在 Post-request 脚本中可用 2. **变量类型** - `pm.environment` 用于持久化变量存储 - `pm.variables` 是临时变量,仅在当前请求生命周期内有效 - `pm.globals` 用于应用级全局变量 - 环境变量会被持久化保存到文件,临时变量不会 3. **断言限制** - 当前仅支持有限的断言方法:`equal`、`eql`、`include`、`property`、`match`、`below` - 不支持完整的 Chai.js 断言库(如 `above`、`length`、`keys`、`true`、`false` 等) - 建议使用简单的 if 判断配合 `throw new Error()` 来实现复杂断言 4. **Cookie 管理** - `pm.cookies` 提供当前请求域的 Cookie 访问 - `pm.cookies.jar()` 可以跨域管理 Cookie,需要完整的 URL - Cookie 操作是异步的,使用回调函数处理结果 5. **类型转换** - 使用 `pm.response.json()` 前确保响应是合法的 JSON 格式 - 环境变量存储时会自动转换为字符串 - 使用 `.toString()` 确保数值类型正确转换 6. **集合操作** - `pm.request.headers`、`formData`、`urlencoded`、`params` 都是 `JsListWrapper` 类型 - 对这些集合的修改会直接影响实际发送的请求(仅在 Pre-request 中有效) - 使用 `add()`、`remove()`、`upsert()` 进行集合操作 7. **内置库** - 支持 `crypto-js`、`lodash`、`moment` 三个内置库 - ✅ **推荐**:直接使用全局变量 `CryptoJS`、`_`、`moment`(已预加载) - ✅ **也支持**:使用 `require('library-name')` 加载 - 库代码会被缓存,重复加载不会影响性能 8. **不支持的功能** - ❌ `pm.iterationData` - 不支持迭代数据(但支持 CSV 数据驱动) - ❌ `pm.info` - 不支持请求元信息访问 - ❌ 完整的 Chai.js 断言库 --- ## 快速参考 ### 常用 API 速查 ```javascript // ===== 环境变量 ===== pm.environment.set('key', 'value') // 设置 pm.environment.get('key') // 获取 pm.environment.has('key') // 检查 pm.environment.unset('key') // 删除 pm.environment.clear() // 清空 // ===== 全局变量 ===== pm.globals.set('key', 'value') // 设置全局变量 pm.globals.get('key') // 获取全局变量 // ===== 临时变量 ===== pm.variables.set('key', 'value') // 设置 pm.variables.get('key') // 获取 pm.variables.has('key') // 检查 pm.variables.unset('key') // 删除 // ===== 请求操作 (Pre-request) ===== pm.request.headers.add({key, value}) // 添加请求头 pm.request.params.add({key, value}) // 添加查询参数 pm.request.formData.add({key, value}) // 添加表单数据 pm.request.url.toString() // 获取 URL // ===== 响应访问 (Post-request) ===== pm.response.code // 状态码 pm.response.status // 状态文本 pm.response.responseTime // 响应时间 pm.response.text() // 响应文本 pm.response.json() // 响应 JSON pm.response.headers.get('name') // 获取响应头 pm.response.size() // 响应大小 // ===== Cookie ===== pm.cookies.get('name') // 获取 Cookie pm.cookies.set({name, value}) // 设置 Cookie pm.cookies.has('name') // 检查 Cookie pm.getResponseCookie('name') // 从响应获取 // ===== 测试断言 ===== pm.test("测试名", function () { // 定义测试 pm.response.to.have.status(200) // 断言状态码 pm.expect(value).to.equal(expected) // 相等断言 pm.expect(str).to.include(substr) // 包含断言 pm.expect(obj).to.have.property('k') // 属性断言 pm.expect(str).to.match(/regex/) // 正则断言 pm.expect(num).to.be.below(max) // 数值断言 }) // ===== 工具方法 ===== pm.uuid() // 生成 UUID pm.getTimestamp() // 获取时间戳 console.log(message) // 输出日志 // ===== 内置库(全局变量,直接使用)===== CryptoJS.MD5('text').toString() // 加密库(全局变量 CryptoJS) _.random(1, 100) // 工具库(全局变量 _) moment().format('YYYY-MM-DD') // 日期库(全局变量 moment) // 也支持 require() var CryptoJS = require('crypto-js') var _ = require('lodash') var moment = require('moment') ``` --- ## 参考资源 - [Postman 官方文档](https://learning.postman.com/docs/writing-scripts/intro-to-scripts/) - [ChaiJS 断言库](https://www.chaijs.com/api/bdd/)