团队管理
团队管理
成员权限分成两个部分:团队权限和成员权限。团队权限指成员对团队操作的权限,项目权限指成员对项目操作的权限。
团队权限
团队角色分所有者、管理者和普通用户,对应权限如下:
权限名称 | 所有者 | 管理员 | 普通成员 |
---|---|---|---|
修改团队资料 | √ | × | × |
移交团队 | √ | × | × |
解散团队 | √ | × | × |
查看成员权限列表 | √ | √ | × |
修改成员权限 | √ | √ | × |
邀请/移出成员 | √ | √ | × |
项目权限
为了满足团队的多层次管理需求,每个成员可以按项目设置管理员、普通成员、只读成员、禁止访问的角色,对应权限如下:
权限名称 | 管理员 | 普通成员 | 只读成员 | 禁止访问 |
---|---|---|---|---|
项目增删改 | √ | × | × | × |
项目信息修改 | √ | × | × | × |
访问接口文档 | √ | √ | √ | × |
接口增删改 | √ | √ | × | × |
接口查看调试 | √ | √ | √ | × |
用例增删改 | √ | √ | × | × |
用例查看和运行 | √ | √ | √ | × |
测试套件增删改 | √ | √ | × | × |
测试套件运行 | √ | √ | √ | × |
数据模型增删改 | √ | √ | × | × |
数据模型查看 | √ | √ | √ | × |
环境增删改 | √ | √ | × | × |
Mock 规则增删改 | √ | √ | × | × |
公共 Response 增删改 | √ | √ | × | × |
公共脚本增删改 | √ | √ | × | × |
数据库连接增删改 | √ | √ | × | × |
自定义函数增删改 | √ | √ | × | × |
变量增删改 | √ | √ | × | × |
变量本地值设置 | √ | √ | √ | × |
导入导出数据 | √ | × | × | × |
团队协作流程
推荐流程
- 前端(或后端)在 Apifox 上定好接口文档初稿。
- 前后端 一起评审、完善接口文档,定好接口用例。
- 前端 使用系统根据接口文档自动生成的 Mock 数据进入开发,无需手写 mock 规则。
- 后端 使用接口用例 调试开发中接口,只要所有接口用例调试通过,接口就开发完成了。如开发过程中接口有变化,调试的时候就自动更新了文档,零成本的保障了接口维护的及时性。
- 后端 每次调试完一个功能就保存为一个接口用例。
- 测试人员 直接使用接口用例测试接口。
- 所有接口开发完成后,测试人员(也可以是后端)使用集合测试功能进行多接口集成测试,完整测试整个接口调用流程。
前后端 都开发完,前端从Mock 数据切换到正式数据,联调通常都会非常顺利,因为前后端双方都完全遵守了接口定义的规范。
自定义字段
自定义字段功能,可以支持到项目管理者,可以根据自己的需要,设置接口文档的通用字段,比如:创建时间、TAPD 链接、需求文档链接等,更方便的管理项目
配置自定义字段-管理者
在项目设置-功能设置处,管理者可以根据项目需要,配置自定义字段:
字段名:该字段的名称
- 字段类型:支持文本、数字、单选、多选、日期、项目成员、链接、邮箱、单选标签、多选标签 (单选、多选支持管理者设置选项)
- 提示语:展示给项目成员,填写内容时给予提示
- 对应 OpenAPI 字段:导入/导出 OpenAPI/Swagger 格式数据时使用,留空则在导入/导出 OpenAPI/Swagger 格式数据时忽略该字段
- 启用:管理者可以根据需要,开启/关闭该自定义字段
填写自定义字段-项目成员
在配置自定义字段并开启后,接口文档-修改文档页面就会出现该字段,项目成员根据要求填写
展示自定义字段
在配置自定义字段并填写后,接口文档-文档页面就会显示出对应的信息
最佳实践
团队管理
团队协作流程
接口之间如何传递数据
使用场景
B 接口请求参数依赖于 A 接口返回的数据,希望 B 接口发送请求的时候能获取 A 接口返回的数据作为请求参数。
实现思路
- A 接口使用后置操作->提取变量功能将请求完成后返回的对应数据提取到变量。
- B 接口对应的参数值直接引用前面设置的变量。
实现示例
一、A 接口添加后置操作提取变量
打开 A 接口用例的后置操作Tab,添加后置操作->提取变量,如下所示:将接口返回 Response JSON 数据里的 token 值提取到名为 token 的临时变量(或环境变量、全局变量)。
二、B 接口参数引用变量
对应的参数值里写入 {{token}},即引用可名为token的变量。登录态(Auth)如何处理
常见处理方式
一、Session/Cookie 方式
Apifox 会自动保持 Session/Cookie 方式的登录态。
使用方法:
- 先执行登录接口,执行完成后全局 Cookie 会自动保存返回的 Session/Cookie 信息。
- 然后运行其他接口,会自动带上 Session/Cookie 信息。
注意 Session/Cookie 方式可以按下文方法实现全自动登录。
二、Token 方式
Token 方式是将登录凭证放在接口请求参数里(通常会放在 Header)。常见的有Basic Auth、Bearer Token、API Key等方式。
使用方法:
- 方法 1. 通过全局(项目概览页)、分组(分组设置)、接口(文档编辑页)的 Auth 设置授权信息,支持如下多种授权类型
- 方法 2. 手动将 token 写入 Header 或其他对应参数里(推荐使用 环境变量 存放 token)。示例:
- Bearer Token:设置一个名为Authorization的 Header,设置值为Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9yJpZCI6(或者使用环境变量,设置值为Bearer {{AUTH_TOKEN}})
- 方法 3. 在 环境 里的全局参数统一设置,所有接口运行时会自动加上全局参数,无需每个接口手动设置。
注意 Token 方式可以按下文方法实现全自动登录。
三、全自动登录
运行接口用例的时候,自动调用登录接口完成登录,而无需手动登录。
使用方法:
- 运行接口用例的时候,自动完成登录,而无需手动登录。
自动登录过一次后,保存登录态,避免每次执行用例都调用登录接口。
实现思路
使用 环境变量(如:ACCESS_TOKEN)保存登录需要的凭证。
- 如凭证有过期时间,使用环境变量(如:ACCESS_TOKEN_EXPIRES)保存登录凭证的过期时间。
- 创建一个 公共脚本 :
- 判断环境变量ACCESS_TOKEN是否有值,以及ACCESS_TOKEN_EXPIRES是否过期,如果存在且未过期,跳出执行,否则下一步。
- 使用 pm.sendRequest 调用登录接口,将登录接口返回的登录凭证写入环境变量,过期时间也写入环境变量。
- 设置需要登录态的接口用例:
- 将用来验证登录态的参数值设置为 {{ACCESS_TOKEN}} 。
- 将 header 里的Authorization的设置为{{ACCESS_TOKEN}}。注意:这里也可以使用 Cookie 或其他位置的参数,请根据实际情况确定。
- 此处也可以在 环境 里的 额外参数 统一设置,所有接口运行时会自动加上 额外参数,无需每个接口手动设置。
- 在 前置脚本 里引用前面创建公共脚本。
公共脚本示例
注意
- 示例脚本里的登录用户名和密码,是从环境变变量 LOGIN_USERNAME 和 LOGIN_PASSWORD 获取,如果你直接拷贝代码的话,记得要手动设置这两个环境变量。
- 如果你们的 token 没有过期时间,可以将 ACCESS_TOKEN_EXPIRES相关的代码去除。
- pm.sendrequest 参考文档
- pm.cookies 参考文档
- 将用来验证登录态的参数值设置为 {{ACCESS_TOKEN}} 。
// 定义发送登录接口请求方法
function sendLoginRequest() {
// 获取环境里的 前置URL
const baseUrl = pm.environment.get("BASE_URL");
// 登录用户名,这里从环境变量 LOGIN_USERNAME 获取,也可以写死(但是不建议)
const username = pm.environment.get("LOGIN_USERNAME");
// 登录用户名,这里从环境变量 LOGIN_PASSWORD 获取,也可以写死(但是不建议)
const password = pm.environment.get("LOGIN_PASSWORD");
// 构造一个 POST x-www-form-urlencoded 格式请求。这里需要改成你们实际登录接口的请求参数。
const loginRequest = {
url: baseUrl + "/api/v1/login",
method: "POST",
// body 为 x-www-form-urlencoded 格式
body: {
mode: "urlencoded", // 此处为 urlencoded
// 此处为 urlencoded
urlencoded: [
{ key: "account", value: "apifox" },
{ key: "password", value: "123456" },
],
},
/*
// body 为 form-data 格式
body: {
mode: 'formdata', // 此处为 formdata
// 此处为 formdata
formdata: [
{ key: 'account', value: 'apifox' },
{ key: 'password', value: '123456' }
]
}
// body 为 json 格式
header: {
"Content-Type": "application/json", // 注意:header 需要加上 Content-Type
},
body: {
mode: 'raw',// 此处为 raw
raw: JSON.stringify({ account: 'apifox', password:'123456' }), // 序列化后的 json 字符串
}
// body 为 raw 或 json 格式
body: {
mode: 'raw',
raw: '此处为 body 内容',
}
*/
};
// 发送请求。
// pm.sendrequest 参考文档: https://www.apifox.cn/help/app/scripts/api-references/pm-reference/#pm-sendrequest
pm.sendRequest(loginRequest, function(err, res) {
if (err) {
console.log(err);
} else {
// 读取接口返回的 json 数据。
// 如果你的 token 信息是存放在 cookie 的,可以使用 res.cookies.get('token') 方式获取。
// cookies 参考文档:https://www.apifox.cn/help/app/scripts/api-references/pm-reference/#pm-cookies
const jsonData = res.json();
// 将 accessToken 写入环境变量 ACCESS_TOKEN
pm.environment.set("ACCESS_TOKEN", jsonData.data.accessToken);
// 将 accessTokenExpires 过期时间写入环境变量 ACCESS_TOKEN_EXPIRES
pm.environment.set(
"ACCESS_TOKEN_EXPIRES",
jsonData.data.accessTokenExpires
);
}
});
}
// 获取环境变量里的 ACCESS_TOKEN
const accessToken = pm.environment.get("ACCESS_TOKEN");
// 获取环境变量里的 ACCESS_TOKEN_EXPIRES
const accessTokenExpires = pm.environment.get("ACCESS_TOKEN_EXPIRES");
// 如 ACCESS_TOKEN 没有值,或 ACCESS_TOKEN_EXPIRES 已过期,则执行发送登录接口请求
if (
!accessToken ||
(accessTokenExpires && new Date(accessTokenExpires) <= new Date())
) {
sendLoginRequest();
}
接口签名如何处理
期望效果
-
实现思路
- 将生成的签名 sign 作为参数加入请求,可使用以下两种方案:
- 方案一:脚本直接修改请求信息,注入一个签名参数,无需使用环境变量。(注:脚本只能修改 header 和 query 参数,不能修改 body 参数。如需将签名放在 body 里,只能使用方案二)。
- 方案二:将生成的签名写入环境变量,接口设置参数时引用环境变量。
- 在接口的 前置脚本 里引用前面创建公共脚本。
如果签名算法是其他语言编写的,可使用fox.execute方法 调用其他语言编写的程序
接口签名示例
签名生成规则
本签名规则同 微信支付签名算法。
第一步:假设所有发送参数集合M,将集合M内非空参数值的参数按照参数名 ASCII 码从小到大排序(字典序),使用 URL 键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:参数名ASCII码从小到大排序(字典序);
- 如果参数的值为空不参与签名;
- 参数名区分大小写;
- 传送的sign参数不参与签名;
第二步:在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
公共脚本实现
// 获取预先设置为环境变量的 APPKEY
let key = pm.environment.get("APPKEY");
// 存放所有需要用来签名的参数
let param = {};
// 加入 query 参数
let queryParams = pm.request.url.query;
queryParams.each(item => {
if (item.value !== '') { // 非空参数值的参数才参与签名
param[item.key] = item.value;
}
});
// 加入 body 参数
if (pm.request.body) {
let formData;
switch (pm.request.body.mode) {
case 'formdata':
formData = pm.request.body.formdata;
break;
case 'urlencoded':
formData = pm.request.body.urlencoded;
break;
case 'raw':
// 如果没有 JSON 格式的请求 body,或 JSON 格式 body 不参与签名,可以删除这一段
let contentType = pm.request.headers.get('content-type');
if (
contentType
&& pm.request.body.raw
&& contentType.toLowerCase().indexOf('application/json') !== -1
) {
try {
let jsonData = JSON.parse(pm.request.body.raw);
/*
* 注意:通过脚本取出来的接口参数,如果参数包含变量,变量是不会替换成对应的值。如想要获取替换后的值,可使用`pm.variables.replaceIn`方法处理:
* let body = pm.variables.replaceIn(pm.request.body.raw);
* let jsonData = JSON.parse(body);
*/
for (let key in jsonData) {
let value = `${jsonData[key]}`; // 此处要注意如果值的实际类型不是 string 需要根据实际情况处理。
if (value !== '') { // 非空参数值的参数才参与签名
param[key] = value;
}
}
} catch (e) {
console.log('请求 body 不是 JSON 格式')
}
}
break;
default:
break;
}
if (formData) {
formData.each(item => {
if (item.value !== '') { // 非空参数值的参数才参与签名
param[item.key] = item.value;
}
});
}
}
// 取 key
let keys = [];
for (let key in param) {
// 注意这里,要剔除掉 sign 参数本身
if (key !== 'sign') {
keys.push(key);
}
}
// 参数名 ASCII 码从小到大排序(字典序)
keys.sort();
// 转成键值对
let paramPair = [];
for (let i = 0, len = keys.length; i < len; i++) {
let k = keys[i];
paramPair.push(k + '=' + encodeURIComponent(param[k])) // urlencode 编码
}
// 最后加上 key
paramPair.push("key=" + key);
// 拼接
let stringSignTemp = paramPair.join('&');
// console.log(stringSignTemp);
let sign = CryptoJS.MD5(stringSignTemp).toString().toUpperCase();
// console.log(sign);
// 方案一:直接修改接口请求的 query 参数,注入 sign,无需使用环境变量。
// 参考文档:https://www.apifox.cn/help/app/scripts/examples/request-handle/
queryParams.upsert({
key: 'sign',
value: sign,
});
// 方案二:写入环境变量,此方案需要在接口里设置参数引用环境变量
// pm.environment.set("SIGN", sign);
百度翻译接口签名示例
本示例为调用百度翻译接口,百度翻译接口参考文档。
签名生成规则
第一步:将请求参数中的 APPID(appid), 翻译query(q, 注意为UTF-8编码), 随机数(salt), 以及平台分配的密钥(可在管理控制台查看) 按照 appid+q+salt+密钥 的顺序拼接得到字符串1。 第二步:对字符串1做md5,得到32位小写的sign。
注:
- 待翻译文本(q)需为UTF-8编码
- 在生成签名拼接 appid+q+salt+密钥 字符串时,q不需要做URL encode,在生成签名之后,发送HTTP请求之前才需要对要发送的待翻译文本字段q做URL encode
官方举例:将apple从英文翻译成中文:
请求参数:
q=apple
from=en
to=zh
appid=2015063000000001
salt=1435660288
平台分配的密钥: 12345678
生成sign:
>拼接字符串1
拼接appid=2015063000000001+q=apple+salt=1435660288+密钥=12345678
得到字符串1 =2015063000000001apple143566028812345678
>计算签名sign(对字符串1做md5加密,注意计算md5之前,串1必须为UTF-8编码)
sign=md5(2015063000000001apple143566028812345678)
sign=f89f9594663708c1605f3d736d01d2d4
完整请求为:
http://api.fanyi.baidu.com/api/trans/vip/translate?q=apple&from=en&to=zh&appid=2015063000000001&salt=1435660288&sign=f89f9594663708c1605f3d736d01d2d4
公共脚本实现
```javascript // 获取 Query 参数对象 var queryParams = pm.request.url.query;
// 获取 query 参数 q 的值 var q = queryParams.get(‘q’);
// 获取预先设置为环境变量的 APPID 和 SECRET_KEY var appid = pm.environment.get(“APPID”); var secretKey = pm.environment.get(“SECRET_KEY”);
// 定义一个随机数(32768, 65536)之间 var salt = parseInt(Math.random() * (32769) + 32768, 10);
// 将随机数转换为字符串 salt = salt.toString(); console.log(salt);
// 定义一个由appid、要翻译的字符串、随机数、密钥组合成一个字符串 var str = appid + q + salt + secretKey; console.log(str);
// 将 str 进行 md5 加密生成 sign var sign = CryptoJS.MD5(str).toString();
// 方案一:直接修改接口请求的 query 参数,注入 salt 和 sign,无需使用环境变量。 // 参考文档:https://www.apifox.cn/help/app/scripts/examples/request-handle/ queryParams.upsert({ key: ‘salt’, value: salt, }); queryParams.upsert({ key: ‘sign’, value: sign, });
// 方案二:将 salt 和 sign 写入环境变量,此方案需要在接口里设置参数引用环境变量 // pm.environment.set(“SALT”, salt); // pm.environment.set(“SIGN”, strmd5);
---
<a name="xx1pY"></a>
# Mock数据
<a name="BltXK"></a>
## Mock 功能说明
前端开发往往依赖于后端数据接口,在后端接口就绪之前,前端通常很难开工。Mock 功能就是用来解决这个问题的。有了 Mock 工具之后,前后端可以同步进入开发,后端接口出来之前,前端可以通过 Mock 功能来制造假数据接口来进行开发和调试。
<a name="cGJM1"></a>
### 一、功能说明
Mock 功能可以根据接口/数据结构定义、Mock规则配置、Mock 期望配置,自动生成模拟数据,且使用者可以根据需要灵活构造各种结构的接口数据。<br />通常情况 Apifox 零配置即可生成非常人性化的 mock 数据:
1. Apifox 根据接口定义里的数据结构、数据类型,自动生成 mock 规则。
1. Apifox 内置 [智能 Mock](https://www.apifox.cn/help/app/mock/intelligent-mock/) 功能,根据字段名、字段数据类型,智能优化自动生成的 mock 规则。如:名称包含字符串image的string类型字段,自动 mock 出一个图片地址 URL;包含字符串time的string类型字段,自动 mock 出一个时间字符串;包含字符串city的string类型字段,自动 mock 出一个城市名。
1. Apifox 根据内置规则(可关闭),可自动识别出图片、头像、用户名、手机号、网址、日期、时间、时间戳、邮箱、省份、城市、地址、IP 等字段,从而 Mock 出非常人性化的数据。
1. 除了内置 mock 规则,用户还可以自定义规则库,满足各种个性化需求。支持使用 正则表达式、通配符 来匹配字段名自定义 mock 规则。
Apifox 零配置 mock 出来的数据效果:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1653142369002-528e8be9-8cef-4e36-9a7b-2fc5371aee13.png#clientId=u6bc8f327-377b-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u7afb7c42&margin=%5Bobject%20Object%5D&name=image.png&originHeight=800&originWidth=1024&originalType=url&ratio=1&rotation=0&showTitle=false&size=460034&status=done&style=none&taskId=ufc64cd0a-2816-4ebf-bb28-b6180c3f9bb&title=)
<a name="MvtSg"></a>
### 二、Mock 请求 URL
Mock URL 支持两种模式:
1. **接口路径模式**:http://127.0.0.1:4523/mock/{项目ID}{接口路径}
1. **接口 ID 模式**:http://127.0.0.1:4523/mock2/{项目ID}/{接口ID}
请求method与接口定义的method保持一致。<br />如你项目 ID 为18600,需要 mock 的接口 ID 为89343,路径为/store/pets,请求method为POST,则实际 Mock URL 为:
```latex
POST http://127.0.0.1:4523/mock/18600/store/pets
或
POST http://127.0.0.1:4523/mock2/18600/89343
默认情况下,定义好接口/数据结构后,无需做任何额外的配置,就可以通过上面的 URL 访问到自动 Mock 出来的数据接口了。
注意
- Mock 服务是在本地启动的,所以 URL 里的 ip 地址为127.0.0.1,如有其他设备需要访问 mock 数据,只需将127.0.0.1改成本机的内网 ip 即可。如果还是访问不了,请检查是否防火墙等限制了 mock 所用的4523端口。
- 如一个项目内,有多个接口拥有相同的method + path路径,则只能使用 接口 ID 模式,不能使用 接口路径模式,否则在会产生路径冲突。
- 如接口路径不是以/起始的,则只能使用 接口 ID 模式,不能使用 接口路径模式。
- 打开 Apifox 就会默认启动 mock 服务,无需额外操作。
- Mock 服务的前置 URL是固定的,不能修改。在名为Mock Server的环境里修改前置 URL并不会修改 Mock 服务的实际前置 URL。
获取接口 mock URL
打开接口详情-查看页面的Mock模块,即可获取对应接口的 mock URLs
三、自定义 Mock 规则
Apifox 支持非常灵活的 mock 规则定义,满足各种业务需求。
1. 数据结构定义 mock 规则
定义数据结构的时候,可手动设置 mock 规则,支持 Mock.js 数据占位符定义方式书写 Mock 规则, 查看 Mock.js 语法
2. 数据字段高级设置
数据字段高级设置里设置的最大值、最小值、枚举值、Partten、format,也会作为 Mock 规则使用:
3. 高级 Mock
高级 mock 的是最灵活的 mock 方式,可实现灵活的自定义数据结构(不受接口数据结构限制),且可以根据不同的请求参数值返回不同的数据。查看高级 Mock 说明文档
4. 智能 Mock
当接口设计的返回 Response (或数据模型) 里的字段未配置 mock 规则时,系统会自动使用智能 Mock 规则生成数据,以实现使用时零配置即可 mock 出非常人性化的数据。查看智能 Mock 说明文档
四、Mock 规则优先级
数据字段在自动 Mock 数据时,实际执行的 Mock 规则优先级顺序如下:
- 接口详情高级 Mock 里设置的期望(根据接口参数匹配)。
- 数据结构的字段里设置的Mock规则。
- 数据结构的字段高级设置里设置的最大值、最小值、枚举值、Partten、format。
- 项目设置-智能 Mock 设置的自定义规则。
- 项目设置-智能 Mock 设置的内置规则。
-
五、其他说明
默认情况下,系统会默认 mock 接口定义里的第一个Response的数据结构,如需 mock 其他Response,可在接口详情-查看页面的Mock模块获取其他 Response的 mock URL。
Mock 语法
Apifox Mock 语法完全兼容 Mock.js(数据占位符方式),并扩展了一些 Mock.js 没有的语法(如国内手机号 @phone)。
注意 如现有 Mock 语法无法满足需求,建议使用 正则表达式 @regexp 来实现灵活的定制。正则表达式基本能满足各种特殊场景的需求。
基本写法
写法 | 说明 |
---|---|
以@起始的字符串 | 调用 Mock 语法规则生成对应的数据。 如生成的数据类型和定义的数据类型不一致,则会自动转换。 |
非@起始的字符串 | 数据类型为string时,原样输出。 其他数据类型,会将字符串自动转换到对应的数据类型。 |
特殊字符:null | 数据类型允许为null 时,输出null。 否则自动转换,如数据类型为string,输出”null”。 |
特殊字符:true | 数据类型为boolean 时,输出true。 否则自动转换,如数据类型为string,输出”true”。 |
特殊字符:false | 数据类型为boolean 时,输出false。 否则自动转换,如数据类型为string,输出”false”。 |
自动转换 是使用 javascript 语言默认数据转换方法进行转换。
Mock 语法
基础类型
分类 | 规则 | 示例 | 示例结果 |
---|---|---|---|
布尔值 | @boolean | @boolean | false true true |
@boolean( min, max, current ) | @boolean(1, 9, true) | false false |
|
自然数 | @natural | @natural | 5748399088025322 3295768519606992 6595528165924058 |
@natural( min ) | @natural(10000) | 5492366038361662 2061541478134547 |
|
@natural( min, max ) | @natural(60, 100) | 99 66 |
|
整数 | @integer | @integer | -7585865973372456 3913822053899244 5164580957595932 |
@integer( min ) | @integer(10000) | 7304069372231661 6538343333105173 |
|
@integer( min, max ) | @integer(60, 100) | 99 -66 |
|
浮点数 | @float | @float | 4791951330736181 3310225074798816.5 -2562681950443352.5 |
@float( min ) | @float(0) | 460523639430244.3 5146972509822378 |
|
@float( min, max ) | @float(60, 100) | 91.85250094787806 66.1232254588188 |
|
@float( min, max, dmin ) | @float(60, 100, 3) | 66.4375611 78.50157676437885 |
|
@float( min, max, dmin, dmax ) | @float(60, 100, 3, 5) | 91.5811 91.27216 |
|
单字符 | @character | “z” “%” “V” |
|
@character( pool ) | @character(‘lower’) | “x” | |
@character(‘upper’) | “R” | ||
@character(‘number’) | “6” | ||
@character(‘symbol’) | “#” | ||
@character( pool ) | @character(‘aeiou’) | “i” “o” | |
字符串 | @string | @string | “2u&x” “rUlmf” “5qfJp1” |
@string( length ) | @string(5) | “88yC6” “3^8VT” | |
@string( pool, length ) | @string(‘lower’, 5) | “xlmes” | |
@string(‘upper’, 5) | “SETVW” | ||
@string(‘number’, 5) | “12751” | ||
@string(‘symbol’, 5) | “%&(#*” | ||
@string(‘aeiou’, 5) | “eeaeu” | ||
@string( min, max ) | @string(7, 10) | “ZIuenM8” “DVu%]kEqC” | |
@string( pool, min, max ) | @string(‘lower’, 1, 3) | “m” | |
@string(‘upper’, 1, 3) | “XXA” | ||
@string(‘number’, 1, 3) | “8” | ||
@string(‘symbol’, 1, 3) | “[“ | ||
@string(‘aeiou’, 1, 3) | “a” |
正则表达式
规则 | 示例 | 示例结果 |
---|---|---|
@regexp( regexp ) | @regexp(/\d+/) | “36436” |
@regexp(/\d{3,5}/) | “343” | |
@regexp(/^[a-zA-Z][A-Za-z0-9_-.]+@gmail.com$/) | “ifa3dt@gmail.com” |
注意
- Apifox 版本号大于等于 1.0.12 才支持正则表达式。
- regexp 参数必须以 / 起始和结尾。
- 正则表达式参考文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
日期/时间
分类 | 规则 | 示例 | 示例结果 |
---|---|---|---|
日期 | @date | @date | “2013-09-05” |
@date( format ) | @date(‘yyyy-MM-dd’) | “1992-10-27” | |
@date(‘yy-MM-dd’) | “04-01-30” | ||
@date(‘y-MM-dd’) | “90-07-29” | ||
@date(‘y-M-d’) | “71-6-4” | ||
@date(‘yyyy yy y MM M dd d’) | “1971 71 71 05 5 02 2” | ||
时间 | @datetime | @datetime | “1988-05-25 11:34:14” |
@datetime( format ) | @datetime(‘yyyy-MM-dd A HH:mm:ss’) | “1978-01-10 AM 03:59:54” | |
@datetime(‘yy-MM-dd a HH:mm:ss’) | “92-06-22 pm 12:45:54” | ||
@datetime(‘y-MM-dd HH:mm:ss’) | “97-02-10 06:01:13” | ||
@datetime(‘y-M-d H: m:s’) | “98-10-17 18:56:12” | ||
当前时间 | @now | @now | |
@now( unit ) | @now(‘year’) | “2020-01-01 00:00:00” | |
@now(‘month’) | “2020-08-01 00:00:00” | ||
@now(‘week’) | “2020-08-09 00:00:00” | ||
@now(‘day’) | “2020-08-11 00:00:00” | ||
@now(‘hour’) | “2020-08-11 15:00:00” | ||
@now(‘minute’) | “2020-08-11 15:24:00” | ||
@now(‘second’) | “2020-08-11 15:24:02” | ||
@now( format ) | @now(‘yyyy-MM-dd HH:mm:ss SS’) | “2020-08-11 15:24:02 761” | |
@now( unit, format ) | @now(‘day’, ‘yyyy-MM-dd HH:mm:ss SS’) | “2020-08-11 00:00:00 000” |
图片
分类 | 规则 | 示例 | 示例结果 |
---|---|---|---|
图片 URL | @image | @image | “http://dummyimage.com/300x250“ |
@image( size ) | @image(‘200x100’) | “http://dummyimage.com/200x100“ | |
@image( size, background ) | @image(‘200x100’, ‘#FF6600’) | “http://dummyimage.com/200x100/FF6600“ | |
@image( size, background, text ) | @image(‘200x100’, ‘#4A7BF7’, ‘Hello’) | “http://dummyimage.com/200x100/4A7BF7&text=Hello“ | |
@image( size, background, foreground, text ) | @image(‘200x100’, ‘#50B347’, ‘#FFF’, ‘Mock.js’) | “http://dummyimage.com/200x100/50B347/FFF&text=Mock.js“ | |
@image( size, background, foreground, format, text ) | @image(‘200x100’, ‘#894FC4’, ‘#FFF’, ‘png’, ‘!’) | “http://dummyimage.com/200x100/894FC4/FFF.png&text=!“ | |
图片数据 | @dataImage | @dataImage | |
@dataImage( size ) | @dataImage(‘200x100’) | ||
@dataImage( size, text ) | @dataImage(‘200x100’, ‘Hello Mock.js!’) |
颜色
规则 | 示例 | 示例结果 |
---|---|---|
@color | @color | “#79aaf2” |
@hex | @hex | “#79f2d0” |
@rgb | @rgb | “rgb(121, 210, 242)” |
@rgba | @rgba | “rgba(121, 242, 167, 0.50)” |
@hsl | @hsl | “hsl(228, 82, 71)” |
中文文本
分类 | 规则 | 示例 | 示例结果 |
---|---|---|---|
段落 | @cparagraph | @cparagraph | “量值这专平约称结半米化目管结北问。次便为流据由经改候究历学始影增具完。流极什其转青次各红团地真产观。争置着安眼头近效例温持完始音加照族。向素海究果存处划化复阶商手。心便近型无院些其认性几步性。” |
@cparagraph( len ) | @cparagraph(2) | “表验目给有新状段步表她位决华响。心什采最结小务受装通米知度是效近。” | |
@cparagraph( min, max ) | @cparagraph(1, 3) | “东例例候专干类济带史还二任头。他千统南常公历快几要只证按规提合中。边也心近号其变代白飞小总红易。” | |
句子 | @csentence | @csentence | “会己风感本员主见它真划生史派达原。” |
@csentence( len ) | @csentence(5) | “化而知只可。” | |
@csentence( min, max ) | @csentence(3, 5) | “称育满。” | |
单词 | @cword | @cword | “光” |
@cword( pool ) | @cword(‘零一二三四五六七八九十’) | “三” | |
@cword( len ) | @cword(5) | “目法小” | |
@cword( pool, length ) | @cword(‘零一二三四五六七八九十’, 3) | “十一九” | |
@cword( min, max ) | @cword(3, 5) | “除文更代” | |
标题 | @ctitle | @ctitle | “清照过做问” |
@ctitle( len ) | @ctitle(5) | “系做组平想” | |
@ctitle( min, max ) | @ctitle(3, 5) | “热群越半” |
中文姓名
规则 | 示例 | 示例结果 |
---|---|---|
@cfirst | @cfirst | “赵” |
@clast | @clast | “秀英” |
@cname | @cname | “乔强” |
Web 相关
规则 | 示例 | 示例结果 |
---|---|---|
“s.piqapshn@qiepsdrrm.jm” | ||
@ip | @ip | “99.34.19.184” |
@url | @url | “gopher://yux.ad/sxte” |
@url( protocol ) | @url(‘http’) | “http://psrvbes.mobi/cxyvc“ |
@domain | @domain | “hrwt.pt” |
@domain( tld ) | @domain(‘com’) | “fdsfl.com” |
@protocol | @protocol | “ftp” |
@tld | @tld | “ga” |
地址相关
分类 | 规则 | 示例 | 示例结果 |
---|---|---|---|
区域 | @region | @region | “华北” |
省 | @province | @province | “陕西省” |
市 | @city | @city | “淮北市” |
市 (含省) | @city( prefix ) | @city(true) | “广东省 肇庆市” |
区 | @county | @county | “东昌区” |
区 (含省市) | @county( prefix ) | @county(true) | “湖南省 怀化市 溆浦县” |
邮编 | @zip | @zip | “843028” |
其他
分类 | 规则 | 示例 | 示例结果 |
---|---|---|---|
GUID | @guid | @guid | “D3f4c7A7-6c6B-1BEA-ff1b-DBfde6Bc1E17” |
数字 ID | @id | @id | “450000197209231877” |
自增 ID | @increment | @increment | “1” “2” “3” |
@increment( step ) | @increment(100) | “101” “201” “301” |
|
首字母大写 | @capitalize( word ) | @capitalize(‘hello’) | “Hello” |
全大写 | @upper( str ) | @upper(‘hello’) | “HELLO” |
全小写 | @lower( str ) | @lower(‘HELLO’) | “hello” |
多选一 | @pick( arr ) | @pick([“a”, “e”, “i”, “o”, “u”]) | “e” |
乱序 | @shuffle( arr ) | @shuffle([“a”, “e”, “i”, “o”, “u”]) | [“e”,”i”,”o”,”a”,”u”] |
英文文本
分类 | 规则 | 示例 | 示例结果 |
---|---|---|---|
段落 | @paragraph | @paragraph | “Befq rkjnyqbc wrflqkzjly tlcdmjm rqpnup ipa fwxkipy bcew nbbnjfrc phroqxn fdhbqdbb cfoute icvcesoar qaetf. Rzurmrdgp wxktybadq byob oqspsewp ljxdrmhnu dwrrqyt mjwqxvrj crd nqcl hudow fokknwhqx aylfpdibdr. Qjwddj seudwxsw bugqajbik johcd bbdslyb natgh vwhbem evayqr csr ftol bjduobs cfiqy uujjldmdgo bsqq. Pttt kdwvhclmn wycb jylskicq jfleydoug gtvlqibx rekmy hmqssqiij fdsmfcs sqmktug cxqun qpecltkv dpdpiqejt lnk.” |
@paragraph( len ) | @paragraph(2) | “Qjcneih hxqtyeyhx crdori puxzq sukv mmoix gyoecfk nqwmvqlg ltvsdpshy qremm awlbgtskx qqzun wppoiasprw ldvlhgh wcyv odotfnggm bgqgcrpwu. Cjyohmm vyrj ehtnlgbg opj budsggflof nilo hlxp wwatulc hunwohq gglluimn mqht audi aepicfkv etpdld pmfuo iotsdbladi shvxfes.” | |
@paragraph( min, max ) | @paragraph(1, 3) | “Mgoyprc hmnscp mrhggsufd jxjpo ggbnxddtqv epdii reuwqs dtyfjmc ppq igcji muudseokx uigz oivhmvbdu csgjfslwc yhu. Mjvln pydscchcrx nnel nxmw edyi ybt kuffpq sjdocykn pxlzem jrjlm pmvck culvj xecywqm oovofkqfwu.” | |
句子 | @sentence | @sentence | “Kgjo tomuhfu phqjx dhmclyl gyyrqpk hgzzrer vjlo cqcr pvkwsogqf ejngmhyuk nsod ouldhpu llkpctcnxs ubzkfu uepyxbkx kopy sdsw.” |
@sentence( len ) | @sentence(5) | “Ktlison axxvc fphbrrn txzoj jupzyrnl.” | |
@sentence( min, max ) | @sentence(3, 5) | “Jzlop lqmkuu cjnrkhge xofrywpe.” | |
单词 | @word | @word | “fers” |
@word( len ) | @word(5) | “qtpjf” | |
@word( min, max ) | @word(3, 5) | “payxj” | |
标题 | @title | @title | “Rvugsxd Jytpw Gqrcffglc Qjt” |
@title( len ) | @title(5) | “Fwbh Udrrncbdpo Xwdqhpu Zvlxyn Fvfnu” | |
@title( min, max ) | @title(3, 5) | “Kwnim Ekvocyml Onxnh” |
英文姓名
规则 | 示例 | 示例结果 | 说明 |
---|---|---|---|
@first | @first | “Michelle” | |
@last | @last | “Lewis” | |
@name | @name | “Charles Williams” |
智能 Mock
当接口设计的返回 Response (或数据模型) 里的字段未配置 mock 规则时,系统会自动使用智能 Mock 规则来生成数据,以实现使用时零配置即可 mock 出非常人性化的数据。
智能 Mock 规则配置
设置位置:项目设置-智能 Mock 设置的自定义规则及内置规则。
- 自定义规则:用户可新建自定义规则,满足各种个性化需求。支持使用 正则表达式、通配符 来匹配字段名自定义 mock 规则。
- 内置规则:系统内置常用 mock 规则库,可自由决定是否开启内置规则。
- 优先级:自定义规则优先级高于内置规则,可添加自定义规则来覆盖系统内置规则。
高级 Mock
Mock 优先级说明
请求 Mock 数据时,规则匹配优先级:高级 Mock 里的期望 > 自定义 Mock 脚本。
如果匹配到了高级 Mock 里的期望,则不调用自定义 Mock 脚本。
Mock 期望
配置项说明:
- 期望条件:根据不同的请求参数,返回不同数据。如创建 2 个 期望:
- 请求参数id为1时,返回销售状态为available的数据。
- 请求参数id为2时,返回销售状态为sold的数据。
- 期望条件支持设置多个参数,多个参数同时匹配时才会匹配到该期望。
- 期望条件支持设置参数名和参数值之间的比较关系,包含:等于、小于、大于、存在、包含等
- 若期望条件里的参数位置选择为body,则实际请求的 body 请求类型需要和该接口定义保持一致,如接口定义的 body 请求类型为form-data,则 mock 时该参数也需要放在form-data里。
- 期望条件 : json 类型的 body 支持使用JSON Path 匹配
- 参数名以 $ 字符起始的,使用 JSON Path 来匹配
- 参数名不是以 $ 字符起始的,直接匹配 JSON 第一级的属性名
返回数据:即接口请求返回的数据,支持 mock.js 、Nunjucks 语法,即可按一定的规则返回动态数据。如下是使用动态模板语法的例子:
{
"data": {
"name": "高级 mock 返回的数据",
"mockJs": [
"@cname",
"@integer(0,100)",
"@image(80x90)"
],
"nunjucks": [
{% for i in range(0, 3) -%}
{
"id": "{{i}}",
"name": "@cname",
"friends": [
{% for item in ['apple','banana','cat', 'dog'] -%}
"{{item}}",
{%- endfor %}
]
},
{%- endfor %}
]
},
"success": true
}
生成数据如下:
{
"data": {
"name": "高级 mock 返回的数据",
"mockJs": ["汪敏", 98, "http://dummyimage.com/80x90"],
"nunjucks": [
{
"id": "0",
"name": "梁强",
"friends": ["apple", "banana", "cat", "dog"]
},
{
"id": "1",
"name": "武秀兰",
"friends": ["apple", "banana", "cat", "dog"]
},
{
"id": "2",
"name": "胡磊",
"friends": ["apple", "banana", "cat", "dog"]
}
]
},
"success": true
}
支持自定义返回 Header、返回 HTTP 状态码、返回延迟。
Mock 自定义脚本
自定义脚本方式可获取用户请求的参数,可修改返回内容。
注意:此处脚本仅用于 高级mock 的 Mock 自定义脚本,不能用于前后置脚本中。
使用方法
- 首先开启此功能
- 使用 JavaScript 脚本修改返回的 JSON 数据,如图
示例一
设置分页数据
// 获取智能 Mock 功能自动 Mock 出来的数据
var responseJson = fox.mockResponse.json();
// 修改 responseJson 里的分页数据
// 将 page 设置为请求参数的 page
responseJson.page = parseInt(fox.mockRequest.getParam('page'));
// 将 total 设置 120
responseJson.total = 120;
// 将修改后的 json 写入 fox.mockResponse
fox.mockResponse.setBody(responseJson);
示例二
var MockJs = require('mockjs');
// 获取“智能Mock”自动生成的 json
var responseJson = fox.mockResponse.json();
// 根据请求参数(包括 query、body、path)修改响应值
if(fox.mockRequest.getParam('id') === '123'){
responseJson.data = null;
responseJson.code = 400104;
responseJson.errorMessage = '数据不存在';
fox.mockResponse.setBody(responseJson);
fox.mockResponse.setCode(404);
}
// 根据请求的 header 修改响应值
if(!fox.mockRequest.headers.get('token')){
responseJson.data = null;
responseJson.code = 400103;
responseJson.errorMessage = '没有权限';
fox.mockResponse.setBody(responseJson);
fox.mockResponse.setCode(403);
}
// 根据请求的 cookie 修改响应值
if(fox.mockRequest.cookies.get('projectId') === '123'){
var idList = [1,2,3,4,5,6,7,8];
fox.mockResponse.setBody({
code: 0,
data: idList.map(function(id){
return {
id: id,
name: MockJs.mock('@cname'),
email: MockJs.mock('@email'),
city: MockJs.mock('@city'),
}
})
});
}
// 设置返回延迟
fox.mockResponse.setDelay(500);
// 添加 header
fox.mockResponse.headers.add({
key: 'X-Token',
value: '<token>',
});
// 添加或修改 header
fox.mockResponse.headers.upsert({
key: 'X-Token',
value: '<token>',
});
请求:fox.mockRequest
- fox.mockRequest.headers 请求的 HTTP 头
- fox.mockRequest.cookies 请求带的 Cookies
fox.mockRequest.getParam(key: string) 获取请求参数,包括 Path 参数、Body 参数、Query 参数。
响应:fox.mockResponse
fox.mockResponse.headers 响应的 HTTP 头
- fox.mockResponse.code 系统自动生成的的 HTTP 状态码
- fox.mockResponse.json() 系统自动生成的 JSON 格式响应数据
- fox.mockResponse.setBody(body: any) 设置接口返回 Body,参数支持 JSON 或字符串
- fox.mockResponse.setCode(code: number) 设置接口返回的 HTTP 状态码
- fox.mockResponse.setDelay(duration: number) 设置 Mock 响应延时,单位为毫秒
使用脚本
脚本介绍
Apifox 包含一个基于Javascript的脚本引擎,通过脚本(JavaScript代码片段)可实现在接口请求或集合测试时添加动态行为。
脚本可实现的功能
- 测试(断言)请求返回结果的正确性(后置脚本)。
- 动态修改接口请求参数,如增加接口签名参数等(前置脚本)。
- 接口请求之间传递数据(使用脚本操作变量)。
- 脚本可以直接 调用其他语言编写的程序,支持java(.jar)、python、php、js、BeanShell、go、shell、ruby、Lua 等语言编写的外部程序。
- 其他。
注意
Apifox 脚本语法100%兼容 Postman脚本语法,Postman 脚本可以无缝迁移到 Apifox。
使用方式
以下两个环节可添加脚本:
在将请求发送到服务器之前,使用 前置脚本。
- 收到响应后,使用 后置脚本(断言测试)。
全局脚本和分组脚本:
- 支持全局设置(在项目概览里设置)前置操作、后置操作,设置后项目里的所有接口运行时都会生效。
- 支持分组里设置前置操作、后置操作,设置后分组里的所有接口运行时都会生效。
接口请求的执行流程如下:
[全局前置脚本] -> [分组前置脚本] -> [接口前置脚本] -> [发送接口请求] -> [返回接口结果] -> [全局后置脚本] -> [分组后置脚本] -> [接口后置脚本]
调试脚本
调试脚本可以在 前置脚本 和 后置脚本 里编写,使用console.log(‘hello’)方式将调试信息写入控制台,打开 控制台 即可查看。
前置脚本
前置脚本是在请求发送前执行的代码片段。如要在请求头中包含时间戳或在 URL 参数中发送随机的字母数字字符串等类似请求中非常适用。
使用示例
要在请求头中包含当前时间戳,可以使用从函数返回的值来设置环境变量。
将参数 timestamp 的值设置为 {{timestamp}} 。当请求发送时,前置脚本将被执行,环境变量 timestamp 的值会被设置为当前时间戳,同时 {{timestamp}}也会被替换为当前时间戳。
注意: 要设置环境变量,该环境必须处于选中状态。
前置脚本使用 JavaScript 编写,语法与后置脚本 完全相同,但不存在pm.response对象。
后置脚本
后置脚本是在请求发送完成后执行的代码片段。主要用来断言请求返回的结果是否正确、将请求返回的结果数据写入环境变量等。
使用示例
断言请求返回的结果是否正确
// pm.response.to.have 示例
pm.test('返回结果状态码为 200', function() {
pm.response.to.have.status(200);
});
// pm.expect() 示例
pm.test('当前为正式环境', function() {
pm.expect(pm.environment.get('env')).to.equal('production');
});
// response assertions 示例
pm.test('返回结果没有错误', function() {
pm.response.to.not.be.error;
pm.response.to.have.jsonBody('');
pm.response.to.not.have.jsonBody('error');
});
// pm.response.to.be* 示例
pm.test('返回结果没有错', function() {
// assert that the status code is 200
pm.response.to.be.ok; // info, success, redirection, clientError, serverError, are other variants
// assert that the response has a valid JSON body
pm.response.to.be.withBody;
pm.response.to.be.json; // this assertion also checks if a body exists, so the above check is not needed
});
将请求返回的结果数据写入环境变量
// 获取 JSON 格式的请求返回数据
var jsonData = pm.response.json();
// 将 jsonData.token 的值写入环境变量
pm.environment.set('token', jsonData.token);
公共脚本
公共脚本主要用途是实现脚本复用,避免多处重复编写相同功能的脚本。
您可以将多处都会用到的相同功能的脚本或者通用的类、方法,放到公共脚本里,然后所有接口直接引用公共脚本即可使用。
使用方式
管理公共脚本
引用公共脚本
接口运行界面或接口用例界面的前置操作和后置操作,可直接引用公共脚本。
注意
- 公共脚本是在普通脚本之前执行的。
- 多个公共脚本执行顺序和添加的顺序保持一致。
普通脚本调用公共脚本
脚本之间是可以做到相互调用的,使用场景:
- 普通脚本需要调用公共脚本里的变量或者方法,注意这种跨脚本调用的方法不要使用 pm.sendRequest 和 pm.environments.set 等设置类型的 API,会失效,建议写纯函数,通过 return 返回。
- 公共脚本之间相互调用。
- 后置脚本和调用前置脚本。
为了避免脚本之间的变量冲突,所有脚本执行的时候都是在各自的作用域(通过闭包包裹)下运行的,而使用var、let、const、function 声明的变量或者方法都是 局部变量或局部方法,所以是不能被其他脚本调用的。如果想要变量或方法被其他脚本调用,需要改成全局变量或全局方法。
变量示例:
// 声明局部变量,无法被其他脚本调用
var my_var = "hello";
要改成
// 声明全局变量,可以被其他脚本调用
my_var = "hello";
方法示例:
// 声明局部方法,无法被其他脚本调用
function my_fun(name) {
console.log("hello" + name);
}
要改成
// 声明全局方法,可以被其他脚本调用
my_fun = function (name) {
console.log("hello" + name);
};
改成全局变量或者全局方法后即可被其他脚本之间调用。
注意
- 请务必注意确保不同脚本之间全局变量或者全局方法命名没有冲突。
- 接口用例,需要在前置脚本或后置脚本里添加了公共脚本才能能调用公共脚本。
- 调用脚本需要注意脚本执行顺序,只有后置的脚本可以调用先执行的脚本。
脚本API参考
pm 对象 API
全局方法
pm
pm:Object
pm对象包含了接口(或测试集)运行的相关信息,并且可以通过它访问需要发送的请求信息和发送后返回的结果信息。另外还可以通过它get或set环境变量和全局变量。
pm.info:Object
pm.info 对象包含了接口(或测试集)运行的相关信息。
- pm.info.eventName:String
当前执行是什么类型的脚本:前置脚本(prerequest),或后置脚本(test)。
- pm.info.iteration:Number
当前执行第几轮循环(iteration),仅集合测试有效。
- pm.info.iterationCount:Number
本次执行需要循环的总轮数,仅集合测试有效。
- pm.info.requestName:String
当前正在运行的接口用例名称
- pm.info.requestId:String
pm.sendRequest
pm.sendRequest:Function
pm.sendRequest 用途为在脚本内异步发送 HTTP/HTTPS 请求。
- 该方法接受一个 collection SDK 兼容的 request 参数和一个 callback 函数参数。 callback 有 2 个参数,第一个是 error ,第二个是 collection SDK 兼容的 response。更多信息请查阅 Collection SDK 文档 。
- 在前置脚本和后置脚本都可以使用。 ```javascript // 简单个 GET 请求示例 pm.sendRequest(“https://postman-echo.com/get“, function(err, res) { if (err) { console.log(err); } else { pm.environment.set(“variable_key”, “new_value”); } });
// 完整的 request 参数示例
const echoPostRequest = {
url: “https://postman-echo.com/post“,
method: “POST”,
header: {
headername1: “value1”,
headername2: “value2”,
},
// body 为 x-www-form-urlencoded 格式
body: {
mode: “urlencoded”, // 此处为 urlencoded
// 此处为 urlencoded
urlencoded: [
{ key: “account”, value: “apifox” },
{ key: “password”, value: “123456” },
],
},
/*
// body 为 form-data 格式
body: {
mode: ‘formdata’, // 此处为 formdata
// 此处为 formdata
formdata: [
{ key: ‘account’, value: ‘apifox’ },
{ key: ‘password’, value: ‘123456’ }
]
}
// body 为 json 格式 header: { “Content-Type”: “application/json”, // 注意:header 需要加上 Content-Type }, body: { mode: ‘raw’,// 此处为 raw raw: JSON.stringify({ account: ‘apifox’, password:’123456’ }), // 序列化后的 json 字符串 }
// body 为 raw 或 json 格式 body: { mode: ‘raw’, raw: ‘此处为 body 内容’, } */ }; pm.sendRequest(echoPostRequest, function(err, res) { console.log(err ? err : res.json()); });
// 对返回结果进行断言 pm.sendRequest(“https://postman-echo.com/get“, function(err, res) { if (err) { console.log(err); } pm.test(“response should be okay to process”, function() { pm.expect(err).to.equal(null); pm.expect(res).to.have.property(“code”, 200); pm.expect(res).to.have.property(“status”, “OK”); }); });
参考:
- [Request JSON 结构](http://www.postmanlabs.com/postman-collection/Request.html#~definition)
- [Response 结构](http://www.postmanlabs.com/postman-collection/Response.html)
<a name="gUszP"></a>
##### pm.variables
pm.variables: [Variable SDK 参考](https://www.postmanlabs.com/postman-collection/Variable.html)<br />临时变量。不同类型的变量,有不同的优先级,不同类型变量的优先级顺序为: 临时变量 < 环境变量 < 全局变量 。
- pm.variables.has(variableName:String):function → Boolean: 检查是否存在某个临时变量。
- pm.variables.get(variableName:String):function → *: get 单个临时变量。
- pm.variables.set(variableName:String, variableValue:String):function → void: set 单个临时变量。
- pm.variables.replaceIn(variableName:String):function: 以真实的值替换字符串里的包含的动态变量,如{{variable_name}}。
- pm.variables.toObject():function → Object: 以对象形式获取所有临时变量。
<a name="d7MgC"></a>
##### pm.iterationData
pm.iterationData:<br />测试数据变量,因为测试数据是单独管理的,暂不支持在脚本中直接设置测试数据变量,但是您可以在脚本中访问测试数据变量,如下。
- pm.iterationData.has(variableName:String):function → Boolean: 检查是否存在某个测试数据变量。
- pm.iterationData.get(variableName:String):function → *: get 单个测试数据变量。
- pm.iterationData.replaceIn(variableName:String):function: 以真实的值替换字符串里的包含的动态变量,如{{variable_name}}。
- pm.iterationData.toObject():function → Object: 以对象形式获取所有测试数据变量。
<a name="Ibu8l"></a>
##### pm.environment
- pm.environment.name:String: 环境名。
- pm.environment.has(variableName:String):function → Boolean:检查是否存在某个环境变量。
- pm.environment.get(variableName:String):function → *:get 单个环境变量。
- pm.environment.set(variableName:String, variableValue:String):function:set 单个环境变量。
- pm.environment.replaceIn(variableName:String):function:以真实的值替换字符串里的包含的动态变量,如{{variable_name}}。
- pm.environment.toObject():function → Object:以对象形式获取当前环境的所有变量。
- pm.environment.unset(variableName:String):function: unset 单个环境变量。
- pm.environment.clear():function:清空当前环境的所有变量。
> **注意:**
> 以上所有操作都是读写的本地值,而不会读写远程值。
<a name="Wsnnk"></a>
##### pm.globals
- pm.globals.has(variableName:String):function → Boolean:检查是否存在某个全局变量。
- pm.globals.get(variableName:String):function → *:get 单个全局变量。
- pm.globals.set(variableName:String, variableValue:String):function:set 单个全局变量。
- pm.globals.replaceIn(variableName:String):function:以真实的值替换字符串里的包含的动态变量,如{{variable_name}}。如前置脚本,获取请求参数的值如果包含变量,则需要使用 pm.globals.replaceIn 才能将变量替换会真正的值。
- pm.globals.toObject():function → Object:以对象形式获取所有全局变量。
- pm.globals.unset(variableName:String):function: unset 单个全局变量。
- pm.globals.clear():function:清空当前环境的全局变量。
> **注意:**
> 以上所有操作都是读写的本地值,而不会读写远程值。
<a name="YJ8Iw"></a>
##### pm.request
pm.request: [Request SDK 参考](https://www.postmanlabs.com/postman-collection/Request.html)<br />request 是接口请求对象。在前置脚本中表示将要发送的请求,在后置脚本中表示`已经发送了的请求。<br />request 包含了以下结构:
- pm.request.url:[Url](http://www.postmanlabs.com/postman-collection/Url.html): 当前请求的 URL。
- pm.request.headers:[HeaderList](http://www.postmanlabs.com/postman-collection/HeaderList.html):当前请求的 headers 列表。
- pm.request.method:String 当前请求的方法,如GET、POST等。
- pm.request.body:[RequestBody](http://www.postmanlabs.com/postman-collection/RequestBody.html): 当前请求的 body 体。
- pm.request.headers.add({ key: headerName:String, value: headerValue:String}):function: 给当前请求添加一个 key 为headerName的 header。
- pm.request.headers.remove(headerName:String):function: 删除当前请求里 key 为headerName的 header
- pm.request.headers.upsert({ key: headerName:String, value: headerValue:String}):function: upsert key 为headerName的 header(如不存在则新增,如已存在则修改)。
**以下部分 API 仅在后置脚本中可用**
<a name="lJ6yr"></a>
##### pm.response
pm.response: [Response SDK 参考](https://www.postmanlabs.com/postman-collection/Response.html)<br />在后置脚本中 pm.response 接口请求完成后返回的 response 信息。<br />response 包含了以下结构:
- pm.response.code:Number
- pm.response.status:String
- pm.response.headers:[HeaderList](http://www.postmanlabs.com/postman-collection/HeaderList.html)
- pm.response.responseTime:Number
- pm.response.responseSize:Number
- pm.response.text():Function → String
- pm.response.json():Function → Object
<a name="Bt6rR"></a>
##### pm.cookies
pm.cookies: [CookieList SDK 参考](https://www.postmanlabs.com/postman-collection/CookieList.html)<br />cookies 为当前请求对应域名下的 cookie 列表。
- pm.cookies.has(cookieName:String):Function → Boolean检查是否存在名为cookieName的 cookie 值
- pm.cookies.get(cookieName:String):Function → Stringget 名为cookieName的 cookie 值
- pm.cookies.toObject:Function → Object以对象形式获取当前域名下所有 cookie
> **注意**
> pm.cookies 为接口请求后返回的 cookie,而不是接口请求发出去的 cookie。
<a name="WDSt0"></a>
##### pm.test
```javascript
pm.test(testName:String, specFunction:Function):Function
该方法用来断言某个结果是否符合预期。
以下示例为检查返回的 respone 是否正确:
pm.test("response should be okay to process", function() {
pm.response.to.not.be.error;
pm.response.to.have.jsonBody("");
pm.response.to.not.have.jsonBody("error");
});
通过 callback 的可选参数 done ,还可用来测试异步方法:
pm.test("async test", function(done) {
setTimeout(() => {
pm.expect(pm.response.code).to.equal(200);
done();
}, 1500);
});
pm.test.index():Function → NumberGet the total number tests from a specific location.
pm.expect
pm.expect(assertion:*):Function → Assertion
pm.expect 是一个普通的断言方法,查看详细的说明:ChaiJS expect BDD library。
该方法用来断言 response 或 variables里的数据非常有用,更多关于 pm.expect断言的是示例,可以点击这里查看:Assertion library examplesResponse 对象可用的断言 API 列表
pm.response.to.have.status(code:Number)
- pm.response.to.have.status(reason:String)
- pm.response.to.have.header(key:String)
- pm.response.to.have.header(key:String, optionalValue:String)
- pm.response.to.have.body()
- pm.response.to.have.body(optionalValue:String)
- pm.response.to.have.body(optionalValue:RegExp)
- pm.response.to.have.jsonBody()
- pm.response.to.have.jsonBody(optionalExpectEqual:Object)
- pm.response.to.have.jsonBody(optionalExpectPath:String)
- pm.response.to.have.jsonBody(optionalExpectPath:String, optionalValue:*)
- pm.response.to.have.jsonSchema(schema:Object)
pm.response.to.have.jsonSchema(schema:Object, ajvOptions:Object)
pm.response.to.be.*
pm.response.to.be 是用来快速断言的一系列内置规则。
pm.response.to.be.info检查状态码是否为1XX
- pm.response.to.be.success检查状态码是否为2XX
- pm.response.to.be.redirection检查状态码是否为3XX
- pm.response.to.be.clientError检查状态码是否为4XX
- pm.response.to.be.serverError检查状态码是否为5XX
- pm.response.to.be.error检查状态码是否为4XX或5XX
- pm.response.to.be.ok检查状态码是否为200
- pm.response.to.be.accepted检查状态码是否为202
- pm.response.to.be.badRequest检查状态码是否为400
- pm.response.to.be.unauthorized检查状态码是否为401
- pm.response.to.be.forbidden检查状态码是否为403
- pm.response.to.be.notFound检查状态码是否为404
- pm.response.to.be.rateLimited检查状态码是否为429
使用 JS 类库
内置的 JS 类库通过 require方法可以直接使用 Apifox 内置的 JS 类库。
var cryptoJs = require("crypto-js");
console.log(cryptoJs.SHA256("Message"));
非内置的 JS 类库通过fox.liveRequire方法可以动态引入未内置的但是在 npm 上已发布的其他各种库(仅支持纯 js 库,最好是写明了支持浏览器端运行的 browser 字样的库,含 C/C++等语言扩展之类的库是不支持加载的,会运行超时或异常)。注意:
- 仅 Apifox 版本号 >= 1.4.5 才支持,老版本不支持,请升级到最新版。
- 非内置库需要动态从网络下载 JS 类库,所以必须要联网,且性能有有所损耗,建议优先使用内置的 JS 库。 ```javascript // 使用非内置的 JS 类库示例
// 引入单个 npm 库:camelcase fox.liveRequire(“camelcase”, (camelCase) => { camelCase(“foo-bar”); // => ‘fooBar’ });
// 引入多个 npm 库:camelcase, fox.liveRequire([“camelcase”, “md5”], (camelCase, md5) => { camelCase(“foo-bar”); // => ‘fooBar’ md5(“message”); // => ‘78e731027d8fd50ed642340b7c9a63b3’ });
<a name="iha2j"></a>
#### 内置类库列表
- Encode、Decode 库
- [atob](https://www.npmjs.com/package/atob)(v2.1.2):Base64 解码
- [btoa](https://www.npmjs.com/package/btoa)(v1.2.1):Base64 编码
- [crypto-js](https://www.npmjs.com/package/crypto-js)(v3.1.9-1):编码 / 解码库,常用的编码解码方式基本都有,如 Base64、MD5、SHA、HMAC、AES 等等。
- 注意:只能 require 整个模块,不能单独 require 类库里的某个子模块,具体看本文档末尾说明。
- [jsrsasign](https://www.npmjs.com/package/jsrsasign)(10.3.0):RSA 加密 / 解密 (Apifox 版本号 >= 1.4.5 才支持,老版本不支持)
- 断言
- [chai](http://chaijs.com/) (v4.2.0):BDD / TDD 断言库
- 实用工具
- [postman-collection](http://www.postmanlabs.com/postman-collection/)( v3.4.0):Postman Collection 库
- [cheerio](https://cheerio.js.org/)(v0.22.0):jQuery 的一个子集
- [lodash](https://lodash.com/) (v4.17.11):JS 实用工具库
- [moment](http://momentjs.com/docs/)(v2.22.2):日期处理库 (不含 locales)
- [uuid](https://www.npmjs.com/package/uuid) :生成 UUID
- [xml2js](https://www.npmjs.com/package/xml2js)(v0.4.19):XML 转 JSON
- [csv-parse/lib/sync](https://csv.js.org/parse/api/sync/)( v1.2.4):CSV 格式数据处理
- JSONSchema 校验库
- [tv4](https://github.com/geraintluff/tv4)(v1.3.0):JSONSchema 校验库
- [ajv](https://www.npmjs.com/package/ajv)(v6.6.2):JSONSchema 校验库
- 内置 NodeJS 模块
- [path](https://nodejs.org/api/path.html)
- [assert](https://nodejs.org/api/assert.html)
- [buffer](https://nodejs.org/api/buffer.html)
- [util](https://nodejs.org/api/util.html)
- [url](https://nodejs.org/api/url.html)
- [punycode](https://nodejs.org/api/punycode.html)
- [querystring](https://nodejs.org/api/querystring.html)
- [string-decoder](https://nodejs.org/api/string_decoder.html)
- [stream](https://nodejs.org/api/stream.html)
- [timers](https://nodejs.org/api/timers.html)
- [events](https://nodejs.org/api/events.html)
<a name="PFcwU"></a>
#### 使用方式
require 对应模块,赋值给一个变量即可使用。
```javascript
// SHA256
var cryptoJs = require("crypto-js");
console.log(cryptoJs.SHA256("Message"));
// base64
var atob = require("atob");
console.log(atob("Message"));
注意 使用内置类库时,只能 require 整个模块,不能单独 require 类库里的某个子模块
// 正确示例:
var cryptoJs = require("crypto-js");
console.log(cryptoJs.SHA256("Message"));
// 错误示例
var SHA256 = require("crypto-js/sha256");
console.log(SHA256("Message"));
脚本调用其他语言( java、python、php 等)
前置脚本和后置脚本,可以直接调用以下语言编写的外部程序:
- java (.jar)
- python (.py)
- php (.php)
- js (.js)
- BeanShell (.bsh)
- go (.go)
- shell (.sh)
- ruby (.rb)
- lua (.lua)
注意
- 仅版本号 >= 1.0.25 的 Apifox 版本支持脚本调用外部程序。
- 外部程序是在沙盒环境以外运行的,有权限访问和操作电脑上的其他程序、文件及数据,存在一定的安全性风险,使用者请务必自己确保被调用程序的安全性。
使用方法
- 将需要调用的外部程序( .jar 、.py、.php等文件 )复制到外部程序目录下。点击软件左下角 ⚙ 形状的 icon ,选择外部程序,即可查看外部程序目录。
- 脚本中使用方法pm.execute(fileName, args)调用外部程序。
- 参数 fileName:String,外部程序文件名,需存放在外部程序目录下。
- 参数 args:Array
,传给外部程序的运行参数,为字符串数组类型,可以传递多个参数。 - 返回值:String,命令行运行程序时,在控制台输出的字符串。
- 发生错误时会抛出异常,建议使用try catch处理异常。
确保电脑已经安装相应程序运行需要的环境。
- .jar程序:需要 安装 java 环境。
- .py程序:需要安装 python 环境。
- .js程序:需要安装 nodejs 环境。
- 其他语言程序:需要安装对应语言的环境。
调用原理
调用外部程序是以命令行的方式运行程序,返回值为程序在控制台输出的字符串。
系统会自动根据外部程序的后缀名,调用对应的命令行来运行外部程序。
- .jar程序:通过java命令运行。
- 如:脚本pm.execute(‘cn.apifox.Base64EncodeDemo.jar’, [‘abc’,’bcd’]),实际执行命令为java -jar cn.apifox.Base64EncodeDemo.jar abc bcd。
- .py程序:通过python命令运行。
- 如:脚本pm.execute(‘md5-json.py’, [‘abc’,’bcd’]),实际执行命令为python md5-json.py abc bcd。
- .js程序:通过node命令运行。
- 如:脚本pm.execute(‘xxx.js’, [‘abc’,’bcd’]),实际执行命令为node xxx.js abc bcd。
- 其他语言程序也是类似原理。
代码示例
后置脚本: ```javascript try { // jar 示例,调用 cn.apifox.Base64EncodeDemo.jar // 实际命令行执行的命令为:java -jar cn.apifox.Base64EncodeDemo.jar abc const jarResult = pm.execute(“cn.apifox.Base64EncodeDemo.jar”, [“abc”]); console.log(“jar 运行结果”, jarResult);
// php 示例,调用 test.php const param1 = { a: 1, b: 2 }; // 注意:json 格式数据作为参数时,需要使用 JSON.stringify 对参数进行序列化 // 实际命令行执行的命令为:php test.php ‘{“a”:1,”b”:2}’ const phpResultString = pm.execute(“test.php”, [JSON.stringify(param1)]); // 注意:返回数据为 json 格式字符串时,可使用 JSON.parse 反序列化 const phpResult = JSON.parse(phpResultString); console.log(“php 运行结果”, phpResult); } catch (e) { console.error(e.message); }
test.php 代码:
php <?php $param = json_decode($argv[1]);- .jar程序:通过java命令运行。
$result = [];
foreach($param as $key=>$value) { $result[$key] = $value * 2; }
echo json_encode($result);
运行结果:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26803611/1653149028088-acfb77b3-d2e3-49cd-9466-08e54e603778.png#clientId=u64993c35-ccd4-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u69a83784&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1316&originWidth=1532&originalType=url&ratio=1&rotation=0&showTitle=false&size=883053&status=done&style=none&taskId=u61d204b0-aa98-42d8-8540-472fd993b21&title=)
<a name="iiaCu"></a>
#### 常见问题
<a name="bNNTb"></a>
##### 1. 引用外部程序输出结果时不同系统返回的字符串可能带有不同换行符号
- 可以对要输出的结果进行去除空格与换行符处理
<a name="KVue8"></a>
## 脚本示例
<a name="YY2vH"></a>
### 断言 (测试请求结果)
后置是在请求发送完成后执行的代码片段。主要用来断言请求返回的结果是否正确、将请求返回的结果数据写入环境变量等。
<a name="hCMRM"></a>
#### 使用示例
<a name="SS7GR"></a>
##### 断言请求返回的结果是否正确
```javascript
// pm.response.to.have 示例
pm.test('返回结果状态码为 200', function() {
pm.response.to.have.status(200);
});
// pm.expect() 示例
pm.test('当前为正式环境', function() {
pm.expect(pm.environment.get('env')).to.equal('production');
});
// response assertions 示例
pm.test('返回结果没有错误', function() {
pm.response.to.not.be.error;
pm.response.to.have.jsonBody('');
pm.response.to.not.have.jsonBody('error');
});
// pm.response.to.be* 示例
pm.test('返回结果没有错', function() {
// assert that the status code is 200
pm.response.to.be.ok; // info, success, redirection, clientError, serverError, are other variants
// assert that the response has a valid JSON body
pm.response.to.be.withBody;
pm.response.to.be.json; // this assertion also checks if a body exists, so the above check is not needed
});
将请求返回的结果数据写入环境变量
// 获取 JSON 格式的请求返回数据
var jsonData = pm.response.json();
// 将 jsonData.token 的值写入环境变量
pm.environment.set('token', jsonData.token);
检查 response body 是否包含某个字符串
pm.test('Body matches string', function() {
pm.expect(pm.response.text()).to.include('string_you_want_to_search');
});
检查 response body 是否包含等于字符串
pm.test('Body is correct', function() {
pm.response.to.have.body('response_body_string');
});
检查 json 值
pm.test('Your test name', function() {
var jsonData = pm.response.json();
pm.expect(jsonData.value).to.eql(100);
});
检查 header 是否有设置 Content-Type
pm.test('Content-Type header is present', function() {
pm.response.to.have.header('Content-Type');
});
检查请求响应耗时是否低于 200 毫秒
pm.test('Response time is less than 200ms', function() {
pm.expect(pm.response.responseTime).to.be.below(200);
});
检查 HTTP 状态码是否为 200
pm.test('Status code is 200', function() {
pm.response.to.have.status(200);
});
检查 HTTP 状态码名称是否包含某个字符串
pm.test('Status code name has string', function() {
pm.response.to.have.status('Created');
});
是否正确的 POST 请求状态码
pm.test('Successful POST request', function() {
pm.expect(pm.response.code).to.be.oneOf([201, 202]);
});
断言库的使用示例
Apifox 内置了ChaiJS作为断言库,以下是常用的断言测试脚本示例,但并非全部示例,更多用法请参考文档: ChaiJS expect BDD library
断言目标字符串包含另一个字符串
pm.test('断言目标字符串包含另一个字符串', function() {
pm.expect('foobar').to.have.string('bar');
});
断言目标严格等于(===)某值
const TEN = 10;
pm.test('Check if number is equal to 10', function() {
pm.expect(TEN).to.equal(10);
});
如果设置了deep标记,则断言目标深度等于value
pm.test('断言目标深度等于提供的 JSON', function() {
pm.expect(data1).to.deep.equal(data2);
});
注意:
设置deep标记,然后使用equal和property断言。该标记可以让其后的断言不是比较对象本身,而是递归比较对象的键值对。
断言深度等于某值,相当于deep.equal(value)的简写
pm.test('Check response value', function() {
var jsonData = pm.response.json();
pm.expect(jsonData.value).to.eql(100);
});
断言当前环境
pm.test('Check if environment is production', function() {
pm.expect(pm.environment.get('env')).to.equal('production');
});
断言数据类型
pm.test('Check if target is string', function() {
pm.expect('Postman').to.be.a('string');
});
pm.test('Check if target is an object', function() {
pm.expect({ a: 1 }).to.be.an('object');
});
pm.test('Check if target is undefined', function() {
pm.expect(undefined).to.be.an('undefined');
});
注意:
推荐在做其他断言前,先使用 .a 方法检查模板的数据类型。
-
断言是否为空
pm.test('Check if array is empty', function() {
pm.expect([]).to.be.empty;
});
pm.test('Check if string is empty', function() {
pm.expect('').to.be.empty;
});
还可以使用 .a方法检查数据类型后,在断言是否为空。
示例:pm.test('Check if array is empty', function() {
pm.expect([]).to.be.an('array').that.is.empty;
});
断言目标对象的键值
pm.test('Check if object contains all provided keys', function() {
pm.expect({ a: 1, b: 2 }).to.have.all.keys('a', 'b');
});
pm.test('Checking if object contains any ONE of the keys', function() {
pm.expect({ a: 1, b: 2 }).to.have.any.keys('a', 'b');
});
pm.test('Check if object contains any NONE of the provided keys', function() {
pm.expect({ a: 1, b: 2 }).to.not.have.any.keys('c', 'd');
});
断言目标对象是否包含指定属性
pm.test('Check if object contains the property', function() {
pm.expect({ a: 1 }).to.have.property('a');
});
注意:
目标对象必须是 object、set、array 或 map。
- 如果 .keys 前面没有 .all 或 .any,则默认为 .all。
由于只有部分数据类型的目标对象可使用 .keys 方法,建议先用 .a方法断言数据类型。
pm.test('Check if object contains all the keys', function() {
pm.expect({ a: 1, b: 2 })
.to.be.an('object')
.that.has.all.keys('a', 'b');
});
断言目标对象的 length
pm.test('Check the length of the target', function() {
pm.expect('foo').to.have.lengthOf(3);
});
pm.test('Check the size of the target', function() {
pm.expect([1, 2, 3]).to.have.lengthOf(2);
});
断言目标对象的成员 (members)
pm.test('Check if the target has same members as the array set', function() {
pm.expect([1, 2, 3]).to.have.members([2, 1, 3]);
});
注意:
默认情况下, .members 使用严格比较。
- members 的顺序不会影响结果。
断言目标对象包含指定 item
注意: 建议在 .include 前先使用 .a 方法判断数据类型。pm.test('Check if the target array includes the number provided', function() {
pm.expect([1, 2, 3]).to.include(2);
});
pm.test(
'Check if the target object includes the properties provided',
function() {
pm.expect({ a: 1, b: 2, c: 3 }).to.include({ a: 1, b: 2 });
},
);
示例:pm.test(
'Check if the target is an array that includes the number specified',
function() {
pm.expect([1, 2, 3])
.to.be.an('array')
.that.includes(2);
},
);
脚本使用变量
环境变量
```javascript // 设置环境变量 pm.environment.set(‘variable_key’, ‘variable_value’);
// 获取环境变量 var variable_key = pm.environment.get(‘variable_key’);
// unset 环境变量 pm.environment.unset(‘variable_key’);
<a name="VTppR"></a>
##### 将对象或数组(非字符串)写入环境变量
环境变量只能存在字符串,如要写入对象或数据,需要使用JSON.stringify转换成字符串
```javascript
var array = [1, 2, 3, 4];
pm.environment.set('array', JSON.stringify(array));
var obj = { a: [1, 2, 3, 4], b: { c: 'val' } };
pm.environment.set('obj', JSON.stringify(obj));
读取的时候,需要使用JSON.parse转换回来
try {
var array = JSON.parse(pm.environment.get('array'));
var obj = JSON.parse(pm.environment.get('obj'));
} catch (e) {
// 处理异常
}
全局变量
// 设置全局变量
pm.globals.set('variable_key', 'variable_value');
// 获取全局变量
var variable_key = pm.globals.get('variable_key');
// unset 全局变量
pm.globals.unset('variable_key');
临时变量
// 设置临时变量
pm.variables.set('variable_key', 'variable_value');
// 获取临时变量
var variable_key = pm.variables.get('variable_key');
// unset 临时变量
pm.variables.unset('variable_key');
脚本读取/修改接口请求信息
脚本如何读取/修改接口请求信息主要使用pm.request。
注意
- 脚本可以读取所有请求参数信息,但是只能修改 header 和 query 参数,不能修改其他参数。
- 只有在前置脚本里修改请求信息才是有效的,在后置脚本里修改无效。
- 通过脚本取出来的接口参数,如果参数包含变量,变量是不会替换成对应的值。如想要获取替换后的值,可使用pm.variables.replaceIn方法处理:
// pm.variables.replaceIn 处理参数里的变量
var body = pm.variables.replaceIn(pm.request.body.raw);
var jsonData = JSON.parse(body);
URL 相关信息
// 获取 url 对象
var urlObj = pm.request.url;
// 获取完整接口请求 URL,包含 query 参数
var url = urlObj.toString();
// 获取协议(http 或 https)
var protocol = urlObj.protocol;
// 获取 端口
var port = urlObj.port;
Header 参数
获取 header 参数
// 获取 Header 参数对象
var headers = pm.request.headers;
// 获取 key 为 field1 的 header 参数的值
var field1 = headers.get("field1");
// 已键值对象方式获取所有 query 参数
var headersObject = headers.toObject();
// 遍历整个 query
headers.each((item) => {
console.log(item.key); // 输出参数名
console.log(item.value); // 输出参数值
});
修改 header 参数
// 获取 Header 参数对象
var headers = pm.request.headers;
// 增加 header 参数
headers.add({
key: "field1",
value: "value1",
});
// 修改 query 参数(如不存在则新增)
headers.upsert({
key: "field2",
value: "value2",
});
Query 参数
获取 query 参数
// 获取 Query 参数对象
var queryParams = pm.request.url.query;
// 获取 key 为 field1 的 query 参数的值
var field1 = queryParams.get("field1");
// 已键值对象方式获取所有 query 参数
var quertParamsObject = queryParams.toObject();
// 遍历整个 query
queryParams.each((item) => {
console.log(item.key); // 输出参数名
console.log(item.value); // 输出参数值
});
修改 query 参数
// 获取 Query 参数对象
var queryParams = pm.request.url.query;
// 增加 query 参数
queryParams.add({
key: "field1",
value: "value1",
});
// 修改 query 参数(如不存在则新增)
queryParams.upsert({
key: "field2",
value: "value2",
});
Body 参数
Body 参数来自pm.request.body,pm.request.body 是一个RequestBody 实例。
参考:http://www.postmanlabs.com/postman-collection/RequestBody.html
注意
- 如需修改 Body 里的数据,推荐在 Body 里引用变量,然后在前置脚本里设置对应变量的值,即可达到修改的目的。
- Body 参数也支持直接修改(版本 >= 1.4.16+),使用方式如下:
var body = pm.request.body.toJSON();
console.log("body 对象", body);
var bodyStr = body.raw;
console.log("body 字符串", bodyStr);
var bodyJSON = JSON.parse(bodyStr);
bodyJSON.id = 100;
pm.request.body.update(JSON.stringify(bodyJSON, null, 2));
console.log("修改后 body", pm.request.body.toJSON());
一、body 类型为 form-data
获取 form-data 信息
// 当 body 类型为 form-data 时,从 pm.request.body.formdata 获取请求参数
var formData = pm.request.body.formdata;
// 获取 key 为 field1 的 form-data 参数的值
var field1 = formData.get("field1");
console.log(field1); // 控制台打印 field1
// 已键值对象方式获取所有 formdata 参数
var formdataObject = formData.toObject();
console.log(formdataObject); // 控制台打印 formdataObject
// 遍历整个 form-data 数据
formData.each((item) => {
console.log(item.key); // 控制台打印参数名
console.log(item.value); // 控制台打印参数值
});
二、body 类型为 x-www-form-urlencode
获取 x-www-form-urlencode 信息
// 当 body 类型为 x-www-form-urlencode** 时,从 pm.request.body.urlencoded 获取请求参数
var formData = pm.request.body.urlencoded;
// 获取 key 为 field1 的 form-data 参数的值
var field1 = formData.get("field1");
// 已键值对象方式获取所有 formdata 参数
var formdataObject = formData.toObject();
// 遍历整个 form 数据
formData.each((item) => {
console.log(item.key); // 控制台打印参数名
console.log(item.value); // 控制台打印参数值
});
三、body 类型为 json
获取 json 信息
// 当 body 类型为 json 时,从 pm.request.body.raw 获取请求参数
try {
var jsonData = JSON.parse(pm.request.body.raw);
console.log(jsonData); // 控制台打印参整个 json 数据
} catch (e) {
console.log(e);
}
四、body 类型为 raw
获取 raw 信息
// 当 body 类型为 raw 时,从 pm.request.body.raw 获取请求参数
var raw = pm.request.body.raw;
console.log(raw); // 控制台打印参整个 raw 数据
其他示例
发送接口请求
pm.sendRequest('https://www.api.com/get', function(err, response) {
console.log(response.json());
});
更多pm.sendRequest用法说明,请查看文档:pm.sendRequest
加密/解密
Decode base64 数据
var cryptoJs = require("crypto-js");
// 假设 `base64Content` 是一个已经用 base64 encoded 过的值
var rawContent = base64Content.slice(
'data:application/octet-stream;base64,'.length,
);
// CryptoJS 是一个已经内嵌到脚本引擎的对象,可以直接使用,使用文档: https://www.npmjs.com/package/crypto-js
var intermediate = cryptoJs.enc.Base64.parse(base64content);
pm.test('Contents are valid', function() {
pm.expect(cryptoJs.enc.Utf8.stringify(intermediate)).to.be.true; // a check for non-emptiness
});
使用 内置的 JS 库 基本可以实现各种加密、解密算法。
转化 XML 为 JSON 对象
var jsonObject = xml2Json(responseBody);