前言
在上一篇 《玩转Postman:基础篇》中,我们介绍了 Postman 工具的主要功能和一些基本用法。 其实 Postman 作为目前使用最为广泛的接口测试工具,除了能提供交互良好的 UI 界面以及完成基本的http协议的鉴权、header、body 等的设置以及请求提交和响应解析这些基本功能外,它还提供了非常丰富的测试辅助能力。本篇我们就来为大家详细介绍 postman 的脚本进阶功能。
Postman的变量
在接口测试工具中,变量对于接口消息的重用和灵活匹配意义重大,作为一个专业的接口测试工具,对变量的支持是必须具备的。
而 Postman 就提供了丰富的变量支持,在 Postman 中定义了5种不同作用范围的变量类型,在变量的使用和管理上更加地灵活和有针对性。下图是官方给出的不同类型变量的作用范围
从外向内作用域逐渐变小,同时生效优先级越高,也就是当存在同一变量名时,内层变量类型的变量会优先生效。下面我们结合实例来具体说明一下这些变量类型的作用范围:
global 变量
global 变量即全局变量,是作用范围最大的一种变量类型。设置好 global 变量后,可以在 Postman 工具中所有可以使用变量的地方生效。
下面在 Postman 工具中看一下 global 变量的设置:
在工具右上角打开环境管理界面:
选择 global 变量管理:
添加 Global 变量:
此例中,我们设置一个 global 变量 name,取值为 1。
在 Postman 中,使用双花括号表示变量,形如 {{ variable }}。以 Github API 为例,比如我们获取用户名为变量 name 取值的用户信息。
url中输入双花括号后会自动联想出我们需要的变量类型。
发送请求后,检查 Postman 响应区,可以看到我们获取了 username 为 1 的用户信息。
json
{
"login": "1",
"id": 1825798,
"node_id": "MDQ6VXNlcjE4MjU3OTg=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1825798?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/1",
"html_url": "https://github.com/1",
"followers_url": "https://api.github.com/users/1/followers",
"following_url": "https://api.github.com/users/1/following{/other_user}",
"gists_url": "https://api.github.com/users/1/gists{/gist_id}",
"starred_url": "https://api.github.com/users/1/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/1/subscriptions",
"organizations_url": "https://api.github.com/users/1/orgs",
"repos_url": "https://api.github.com/users/1/repos",
"events_url": "https://api.github.com/users/1/events{/privacy}",
"received_events_url": "https://api.github.com/users/1/received_events",
"type": "User",
"site_admin": false,
"name": "Michael",
"company": null,
"blog": "",
"location": "San Francisco, CA",
"email": "mbalaban1989@gmail.com",
"hireable": null,
"bio": null,
"public_repos": 3,
"public_gists": 0,
"followers": 15,
"following": 0,
"created_at": "2012-06-07T06:10:07Z",
"updated_at": "2019-01-17T08:29:21Z"
}
Collection 与 Collection 变量
再来看另一个变量类型 Collection 变量。首先了解下 Collection 的概念。Collection 是 Postman 中组织接口的一个集合单位,Postman 中也主要以 Collection 为配置存储的一个基本单位。我们可以把 Collection 看作软件测试中测试用例集的概念。
Collection 变量就是作用域在 Collection 上的变量类型,这种变量只会在设置变量的 Collection 上生效。
设置方法:选择 Edit Collection
在 variable 页中添加 Collection 变量,本例中我们在 Github 这个 Collection 添加一个同样命名为name 的变量
将上例中获取用户的接口保存到 Github 这个 Collection 中(我们可以在 Collection 下再创建一层子目录,注意目录是不支持设置目录级别的变量的,Collection 变量在子目录下的接口依然会生效),我们再提交一下这个接口
此时可以看到,我们设置的 Collection 变量已经生效,获取到的是 name 为 2 的用户信息。注意此时我们还设置了一个 name 为 1 的 global 变量,可以看到 Collection 变量的优先级高于 global变量。
环境和 environment 变量
环境是 Postman 中的一个非常有用的概念。做过软件测试的同学都了解,我们在实际工作中会接触到不同的软件环境。对应于我们被测试系统的不同运行场景。比如一般互联网企业在研发中会存在以下的一些不同环境:
- 开发环境
- 集成环境(联调环境)
- 系统测试环境
- 试生产环境
- 生产环境
- 性能测试环境
- 安全测试环境
不同的环境在接入途径、网络拓扑、访问权限以及硬件配置上往往都有较大区别。Postman 中引入 Environment 这个概念,通过 Environment 变量来管理一组环境配置,便于我们来方便地在不同环境间进行切换。
在环境管理界面中,可以添加环境,以及该环境对应的相关变量。本例我们添加一个 GitChat 环境,并且设置一个 name = 3 的环境变量。
设置完环境后,我们再刚才的获取用户接口界面上,选择对应的 GitChat 环境,再重新提交请求。可以看到环境变量已经生效,获取了name为 3 的用户信息,同样可以看到,此时环境变量的优先级比 Collection 又要更高。
data 变量
另一种 Postman 中的变量类型是 data 变量,data 变量只能在 Postman Runner 中使用,也就是会在 Runner 运行时才生效,data 变量可以提供多组测试数据供接口测试时调用,为 Postman 提供接口批量数据验证能力。要使用 data 变量,打开 Postman Runner, 如下图,选择 data 变量定义文件加载 data 变量文件。
data 变量支持json或者txt/csv数据格式,json 定义格式如下,
json
[
{
"id":"11",
"name":"user1"
},
{
"id":"12",
"name":"user2"
},
{
"id":"13",
"name":"user3"
}
]
txt/csv 格式如下:
shell
id,name
11,user1
12,user2
13,user3
在 runner 界面上可以通过 preview 查看运行时不同迭代所加载的变量取值,如下:
如本例,点击 Run variables 会执行 3 次不同的迭代,通过 Postman Console查看运行日志,可以看到每次均使用了 data 变量中定义的对应变量值。 同样,虽然我们选择 environment,但可以看到 data 变量在 runner 运行时优先级更高。
local 变量
Local 变量在 Postman 官方文档中并没有给出明确的定义。一般可以理解成 Postman 脚本中支持的 JS 变量,它的作用域只会在脚本中生效, 此时 Postman 界面引用的 {{variable}} 并不会取到 local 变量值。
脚本中直接引用的变量名会取local变量,其他数据类型则通过 Postman 对应的取值语句来获取。
比如我们在 Pre-Request 中定义以下脚本并执行:
在 Console 中查看结果如下,可以比较清楚地看到每次执行在预执行脚本中不同变量的当前取值
通过以上实例,我们可以看到,Postman 针对不同的接口作用范围支持了丰富的变量类型,使我们在应用变量功能的时候可以更加的灵活
Postman 脚本及其执行顺序
除了支持丰富的变量功能,Postman 还支持强大的脚本功能,测试人员在进行接口测试时,可以通过脚本来动态地对接口测试逻辑进行定制,再结合变量,就可以实现一些复杂的场景。
Postman 的脚本功能是基于 Node.js 语言,Node.js 成熟的语法和丰富的扩展库给 Postman 提供了巨大的灵活性和强力的扩展能力。
在 Postman 中,测试脚本 可以在接口发送之前和收到响应之后执行,分别对应 Pre-request Script 和 Test Script,如下图:
比如我们前文介绍变量时的实例,其实就是一个 pre-request 脚本
除了接口本身可以设置 pre-request 和 Test Script,我们在编辑 Collection 和 Collection 下的folder时,可以看到 Collection 和 Folder 也都支持设置 pre-request 和 test 脚本。 那这几种脚本的执行优先级或者说顺序是如何的呢? 下图就是这几个不同位置脚本的调用执行顺序:
对TestNG 或者 Junit 等测试框架有所了解的同学,应该知道这些测试框架也有类似的运行时概念,也就是 Setup 和 teardown 方法,而且也有 case、class、suite 这样的层次。不过这些不同层次的方法都是成对出现的,即 case 的 setup 和 teardown 在 case 执行前和结束后会执行。 而 Postman 的 Script 执行顺序和这个稍有差别,是按层级顺序排列,而不是成对出现的。大家参照图片注意理解下差异。
大家也可以自己验证下这个执行顺序,分别在Collection、folder 以及 folder 下的请求中定义相关脚本,如:
执行,查看 console 输出:
可见 Postman 对不同脚本的执行顺序和前文所述一致。
Postman脚本-PreRequest
Pre-request 脚本,顾名思义,其实就是在接口消息发送前,可以进行一些预处理动作,类似于 Junit 或 TestNG 等测试框架中的 Setup 方法。 利用 Pre-Script 脚本,我们就可以在发送接口请求前来完成一些需要动态处理的动作,比如调整变量取值,或者对一些动态参数进行一些特殊处理。
下面我们就以 GitHub API 中的一个实例来看下 Pre-Script 脚本的主要作用。(关于GitHub API 的一些具体说明,大家可以先看下我在上一篇 Chat 《玩转Postman:基础篇》中的介绍)
GitHub API 中一个经常使用到的接口是查询接口
根据 GitHub API 官网的定义说明,查询 repositories 的接口定义如下:
GET /search/repositories
其中参数 q 是主要的查询参数,具体定义参见
https://help.github.com/articles/searching-for-repositories/
比如我们要查询包含在指定日期 2018-11-11 后创建且包含关键字 automation 的 repository 信息。
可知 GitHub 上存在这样的 repo 数量有七千多个。
在这个例子中,因为查询条件包含一个日期参数,而实际测试工作场景中,很多时候我们希望日期是动态生成的,比如根据当前日期取一年以前作为查询参数值,这时我们就需要对参数进行一些预处理,这就是 Pre-Script 的用武之地了。
这时我们可以首先设置一个环境变量 created,再在 pre-script 脚本中动态预处理这个日期,来完成日期动态设置的目的。
javascript
pm.environment.set("created",getCreated(new Date(), -1) );
/*
获取"YYYY-MM-DD"格式的日期
第一个入参为日期对象
第二个入参为年份偏移量,负数为向前偏移
*/
function getCreated(date, yearOffset) {
var seperator1 = "-";
var year = date.getFullYear() + yearOffset;
var month = date.getMonth() + 1;
var day = date.getDate();
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if (day >= 0 && day <= 9) {
day = "0" + day;
}
var currentDate = year + seperator1 + month + seperator1 + day;
console.log(currentDate)
return currentDate;
}
在 Postman中执行如图,获取当前日期一年以来创建的 repositories 信息。
在 Pre-Script中,主要是围绕 Postman 的变量来进行预处理,所以主要使用的就是 Postman 的变量域的一些封装方法。
- pm.globals.has(variableName:String)
是否存在某全局变量
- pm.globals.get(variableName:String)
获取全局变量
- pm.globals.set(variableName:String, variableValue:String)
设置全局变量
- pm.globals.unset(variableName:String)
取消当前全局变量设置
- pm.globals.clear()
清空全局变量
- pm.environment.has(variableName:String)
是否存在某环境变量
- pm.environment.get(variableName:String)
获取环境变量
- pm.environment.set(variableName:String, variableValue:String)
设置环境变量
- pm.environment.unset(variableName:String)
取消当前环境变量设置
- pm.environment.clear()
清空环境变量
- pm.variables.get(variableName:String)
根据变量名获取变量
- pm.sendRequest()
发送请求
Postman脚本-Test脚本
Postman 的 Test 脚本是 Postman 另一个值得称道的特色功能,在 Test 脚本中,Postman 封装了很多丰富的校验逻辑,并结合 JS 脚本本身语言的灵活性,给测试人员对接口的判断、校验和响应处理带来极大的便利性。
下面我们重点介绍下 Test 脚本中封装一些主要的校验方法。
判断接口响应状态码
对接口响应状态码的校验,是接口测试校验的常用手段。关于状态码的详细说明也可参见我的上一篇 Chat 《玩转Postman:基础篇》 , 下面看一个 Postman 中进行接口校验码校验的代码:
javascript
pm.test("判断返回成功状态码",function(){
pm.response.to.have.status(200);
});
或者也可以使用第三方校验库 chaijs 的expect 方法来进行校验。 Chaijs 对常用的校验方式按照行为驱动开发(BDD)的描述方式进行了封装,使校验判断的代码编写方便了很多。
javascript
pm.test("expect方法判断返回成功",function(){
pm.expect(pm.response.code).to.equal(200)
});
再或者我们还可以直接使用 Postman 对返回状态码的封装方法来进行判断
javascript
pm.test("Postman封装方法判断返回成功",function(){
pm.response.to.be.ok
});
Postman 中直接封装了常见的返回状态校验方法:
校验 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 的 OK 返回
- 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 的访问频次限制错误
校验响应时间
除了对于返回码的校验,我们还会经常校验的一个响应指标是响应时间。Postman 对于响应时间的校验也非常简单
javascript
pm.test("响应时间小于1s", function () {
pm.expect(pm.response.responseTime).to.be.below(1000);
});
校验消息内容
对接口返回内容的校验是我们判断业务逻辑正确性与否的必要手段。响应的消息头或者消息体内容我们可以通过 pm.response.header
或者 pm.response.text
、 pm.response.json
来获取。在相应的校验代码中我们就可以根据获取到的内容来进一步判断响应的正确性
javascript
pm.test("校验消息头Content-Type", function () {
pm.expect(postman.getResponseHeader("Content-Type")).to.include("application/json")
});
pm.test("校验消息体返回数量大于1000", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.total_count).to.gt(1000)
});
在 Postman 中执行以上校验,可以在 Response 的 Test Results 中看到校验结果
Postman脚本-接口的关联
在接口测试中,经常出现的一种情况是:我们需要从另一个接口的响应中获取某些值作为当前测试接口的输入来使用。结合 Postman 的 Pre-Script 脚本和 Test 脚本以及变量功能,我们可以方便地完成内容获取和参数传递的场景。
以下面的场景为例:
从 Junit5 这个 repo 的接口信息中,获取该 repo 的创建时间,再查询在这个时间之后创建的所有包含 Junit5 字样的 repo 信息,判断 repo 数量是否超过 1000
方法一:
- 设置一个环境变量 created
- 在获取 repo 接口 GetRepo 的 Test 脚本中获取 Junit5 这个 repo 的创建时间
- 在 Test 脚本中将获取到的创建时间赋值给环境变量 created
- 在查询 repo 接口中使用环境变量 created
- 在查询 repo 接口中添加判断返回数量的校验代码
- 通过 Runner 执行器按顺序执行这两个接口,完成关联执行
如图:
在 Runner 执行器中执行结果
方法二:
在方法一中我们利用环境变量来传递参数,这种方法需要依赖 runner 执行器对接口的执行顺序来保证 GetRepo 接口在 SearchRepo 接口之前执行。
我们也可以利用 Postman 提供的请求发送方法 pm.sendRequest
在 SearchRepo 的 pre-Script 脚本中直接完成前置请求的发送和参数获取。
javascript
pm.sendRequest('https://api.github.com/repos/junit-team/junit5', function (err, res) {
if (err) {
console.log(err);
} else {
pm.environment.set("created", res.json().created_at);
}
});
此时不需要利用 runner 执行器,直接执行 SearchRepo 接口,也可得到同样的结果
Postman脚本-代码复用
Postman 作为一个接口测试工具而不是专业的代码编辑 IDE, 并没有提供脚本的复用以及代码片、模块定义之类的功能。但借助 Postman 强大的变量和 js 语法的良好支持,我们可以变通地实现代码复用。
我们可以将一些常用的代码片段保存到 global 变量中, 在需要使用的时候,直接调用这个 global 变量即可。
比如上面判断返回 repo 数量是否超过 1000 这段校验代码:
javascript
pm.test("判断返回成功状态码",function(){
pm.response.to.have.status(200);
});
pm.test("校验消息体返回数量大于1000", function () {
var jsonData = pm.response.json();
pm.expect(jsonData.total_count).to.gt(1000)
});
我们将它设置在 global 变量 checkRepoCount 中,在需要调用的地方,以如下代码代替即可:
javascript
eval(globals.checkRepoCount)
Postman 中如图:
Postman脚本-一个结合第三方库的复杂场景案例
Postman 为便于接口脚本的编写,内建支持了丰富的第三方库,官方文档中列出了详细清单
除了前面提到的 BDD 校验库 chai, 下面我们再结合第三方时间处理库 moment 来完成一个相对复杂的场景案例实现:
1. 获取最近 6 个月包含有 Junit5 字样且 Star 数 >1 的 repo 信息
2. 判断返回 repo 数量是否 >0, 结果显示到 Test Result
3. 如果数量 >0 , 获取 star 数最多的 repo 信息
4. 判断自己是否已经 star 过这个repo
5. 如果没有 star 过,执行 star操作
相关的接口如下:
- 查询接口,查询关键字 junit5,star 数 >1 , 创建时间 6 月内, 按 star 数降序查询
/search/repositories?q=junit5+stars:>1+created:>{{created}}&sort=stars
- 查询是否 star 了某 repo
GET /user/starred/:owner/:repo
已 star 返回 204,未 star 返回 404
- 执行 star repo 操作
PUT /user/starred/:owner/:repo
成功则返回 204
在查询repo接口的 Pre-Script 脚本中引入事件处理库 moment,可以看到 moment 库相比 js 本身的 date 方法,对日期的处理更加方法灵活,利用 subtract 方法可简单获取半年前的日期,无需考虑日期补零、跨年等判断,代码如下:
javascript
var m = require("moment")
//利用 moment 库获取六个月前日期
let createDate = m(m.now()).subtract(6,"months").format("YYYY-MM-DD")
pm.environment.set("created", createDate);
在查询repo接口的 Test 脚本中编写校验和后续逻辑代码:
javascript
//获取符合条件repo总数量
let count = pm.response.json().total_count
pm.test("覆盖条件repo数量为 "+count, function(){
if( count > 0 ){
//保存repo的owner和repo名称,查询条件为按star排序,所以index为0的repo即star数最多
var repoName = pm.response.json().items[0].name
var owner = pm.response.json().items[0].owner.login
pm.test("当前star数最多的repo是【"+ pm.response.json().items[0].full_name + "】", function(){
//需要定义请求信息,指定header和执行方法。不指定的话直接在SendRequest中使用默认为GET方法,不携带Header
const getStarReq = {
url: "https://api.github.com/user/starred/"+owner+"/"+repoName,
method: 'GET',
//接口需要经过鉴权,代码中不能使用Postman界面设置,将鉴权信息携带在header中
header:'Authorization:Bearer 84fb9e0706bab75f1d4b6f4e3586683d8c4605af'
}
pm.sendRequest(getStarReq, function (err, res) {
if (err) {
console.log(err);
} else {
let stared = pm.test("当前repo已star",function(){
if (res.code === 404){
const starRequest = {
url: "https://api.github.com/user/starred/"+owner+"/"+repoName,
method: 'PUT',
//接口需要经过鉴权,代码中不能使用Postman界面设置,将鉴权信息携带在header中
header: 'Authorization:Bearer 84fb9e0706bab75f1d4b6f4e3586683d8c4605af'
}
pm.sendRequest(starRequest, function (starErr, starRes){
if (starErr) {
console.log(starErr);
} else {
pm.test("已star成功",function(){
pm.expect(starRes.code).to.equal(204)
})
}
})
}
pm.expect(res.code).to.equal(204)
})
}
})
})
}
})
Postman 中执行效果如图:
结语及预告
以上就是关于 Postman 的脚本进阶使用的介绍。简单总结一下:
-
Postman 提供了 5 种不同的变量类型,且分别对应不同的作用域。灵活使用这些变量,可以帮助我们实现动态匹配、参数传递、代码复用等场景。
-
postman 在 Collection、Folder、接口本身三层上都分别提供了两种脚本沙箱:Pre-Script、Test。Pre-Script是在接口请求之前执行,Test 是在接口响应之后执行。注意三层对象上不同的执行顺序。
-
Postman 脚本基于 Node.js,内建支持了丰富的第三方库,并且 Postman 自身也封装了很多上层方法,可以参考 Postman 官方沙箱方法参考
所以Postman 通过丰富的变量以及强大的 js 脚本支持,可以非常灵活地帮助我们更好地完成接口测试中一些复杂的场景。
虽然目前两篇 Chat 主要介绍了 Postman 在接口手工测试中的主要功能和使用技巧,但 Postman 其实也能非常方便地应用在自动化测试中,欢迎大家继续关注下一篇 《玩转Postman:自动化篇》,我们将一起来学习:
- Postman 的批量执行工具 Runner 详解
- Postman 实现接口的流程控制
- Postman 的命令行自动化工具 NewMan 详解
- 结合持续集成工具 Jenkins 实现 Postman 接口脚本的自动调度和执行