四、脚本
Postman基于Node.js开发了一个强大的运行脚本平台,可以在请求和集合运行前后添加更多的动态操作,你可以使用它来编写测试用例、构建请求,你可以在下面两个事件执行的时候添加JavaScript代码:
- 在请求发送到服务器之前,可以在 Pre-request Script 标签页添加前置脚本
- 在响应到达之后,可以在 Tests 标签页上添加测试脚本
你不仅可以在每个请求中添加脚本,还可以在文件夹、集合中添加脚本,脚本的执行顺序如下:
在脚本中使用 console.log('...')
等打印日志的代码时,会在Postman的控制台输出,可以点击左下角的控制台(Ctrl+Alt+C
)图标打开:
Postman的脚本执行原理是Postman基于Node.js开发,并自带一个沙箱环境来运行JavaScript脚本
1. 前置脚本
前置脚本在发送请求之前执行,可以用来定义参数构建请求,比如在发送的请求中添加一个时间请求头:
- 在请求的 Pre-request Script 标签中编写下面代码:
pm.globals.set("timestampHeader", new Date());
在请求头 Headers 标签添加timestamp请求头,并使用{{timestampHeader}}
来使用变量
不管是全局变量还是环境变量,还是集合的变量,都可以在URL、表单、请求体等输入框通过 {{var}}
来使用变量。
2. 测试脚本
使用JavaScript语言来为每个请求编写测试。
在Postman发送请求之后,可以访问响应对象 pm.response
,例如:
// pm.response.to.have pm.test("response is ok", function () { pm.response.to.have.status(200); }); // pm.expect() pm.test("environment to be production", function () { pm.expect(pm.environment.get("env")).to.equal("production"); }); // response 断言 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"); }); // pm.response.to.be* pm.test("response must be valid and have a body", function () { pm.response.to.be.ok; pm.response.to.be.withBody; pm.response.to.be.json; });
在编写脚本的时候虽然你只需要记住很少的一些代码,Postman在编辑栏也提供了常用的代码片段,来帮助你更快的编写脚本。
在响应的 Tests 标签栏可以看到所有测试的结果。
在编辑集合或者文件夹的时候可以添加脚本。当你运行一个集合或文件夹时,里面包含多个请求,可以使用脚本来控制请求的工作流:
设置当前请求执行完的下一个请求
postman.setNextRequest("request_name");
设置当前请求执行完后停止请求
postman.setNextRequest(null);
使用 postman.setNextRequest()
注意的点:
- 指定后续请求的名称或ID,然后使用 Runner 来执行
- 它可以在前置脚本和测试脚本中使用,如果指定了多个,只有最后一个有效
- 如果没有
postman.setNextRequest()
,Runner将按顺序执行后续请求
3. 测试示例
下面是常用的测试脚本:
设置环境变量
pm.environment.set("variable_key", "variable_value");
设置嵌套对象环境变量
var array = [1, 2, 3, 4]; pm.environment.set("array", JSON.stringify(array, null, 2)); var obj = { a: [1, 2, 3, 4], b: { c: 'val' } }; pm.environment.set("obj", JSON.stringify(obj));
获取环境变量
pm.environment.get("variable_key");
获取嵌套对象环境变量
var array = JSON.parse(pm.environment.get("array")); var obj = JSON.parse(pm.environment.get("obj"));
清除环境变量
pm.environment.unset("variable_key");
设置全局变量
pm.globals.set("variable_key", "variable_value");
获取全局变量
pm.globals.get("variable_key");
清除全局变量
pm.globals.unset("variable_key");
获取变量:从环境变量和全局变量中搜索
pm.variables.get("variable_key");
检验响应体中是否包含字符串
pm.test("Body matches string", function () { pm.expect(pm.response.text()).to.include("string_you_want_to_search"); });
检验响应体中是否等于字符串
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); });
检验是否存在Content-Type
pm.test("Content-Type 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); });
检验状态码是否为200
pm.test("Status code is 200", function () { pm.response.to.have.status(200); });
检验状态码名称包含字符串
pm.test("Status code name has string", function () { pm.response.to.have.status("Created"); }); 检验状态码是否在某个范围内
验状态码是否在某个范围内
pm.test("Successful POST request", function () { pm.expect(pm.response.code).to.be.oneOf([201, 202]); });
使用TinyValidator校验JSON数据
var schema = { "items": { "type": "boolean" } }; var data1 = [true, false]; var data2 = [true, 123]; pm.test('Schema is valid', function () { pm.expect(tv4.validate(data1, schema)).to.be.true; pm.expect(tv4.validate(data2, schema)).to.be.true; });
JSON校验
var Ajv = require('ajv'), ajv = new Ajv({ logger: console }), schema = { "properties": { "alpha": { "type": "boolean" } } }; pm.test('Schema is valid', function () { pm.expect(ajv.validate(schema, { alpha: true })).to.be.true; pm.expect(ajv.validate(schema, { alpha: 123 })).to.be.false; });
base64解码
var intermediate, base64Content, rawContent = base64Content.slice('data:application/octet-stream;base64,'.length); intermediate = CryptoJS.enc.Base64.parse(base64content); pm.test('Contents are valid', function () { pm.expect(CryptoJS.enc.Utf8.stringify(intermediate)).to.be.true; });
发送异步请求:该函数可以用于前置脚本和测试脚本中
pm.sendRequest("https://postman-echo.com/get", function (err, response) { console.log(response.json()); });
XML转换成JSON对象
var jsonObject = xml2Json(responseBody);
4. 脚本API
Postman脚本提供了简单实用的API用于测试和工作流。
4.1 require
require(moduleName:String):function → *
require
函数可以导入Postman沙箱内置的模块库,包含下列内置库:
- ajv → v6.6.2
- atob → v2.1.2
- btoa → v1.2.1
- chai → v4.2.0
- cheerio → v0.22.0
- crypto-js → v3.1.9-1
- csv-parse/lib/sync → v1.2.4
- lodash → v4.17.11 (when used with require, the inbuilt
_
object is for v3.10.1) - moment → v2.22.2 (sans locales)
- postman-collection → v3.4.0
- tv4 → v1.3.0
- uuid → (the module loaded is a shim for original module)
- xml2js → v0.4.19
一些NodeJS的模块也可以使用:
- path
- assert
- buffer
- util
- url
- punycode
- querystring
- string_decoder
- stream
- timers
- events
使用函数导入模块赋值给变量,例如:
var atob = require('atob'), _ = require('lodash'), arrayOfStrings, base64Strings; arrayOfStrings = ['string1', 'string2']; base64Strings = _.map(arrayOfStrings, atob); console.log(base64Strings);
4.2 pm
pm:Object
pm
对象封装了有关脚本执行的所有信息,可以用来获取请求、响应信息,也可以获取和设置环境环境变量和全局变量。
pm.info:Object
pm.info
对象包含了脚本执行时的信息,比如请求名称,请求ID和迭代次数都保存在这对象中。
pm.info.eventName:String
:执行脚本名称,前置脚本:prerequest,测试脚本:testpm.info.iteration:Number
:Runner执行时的当前迭代计数pm.info.iterationCount:Number
:迭代执行时的迭代总数pm.info.requestName:String
pm.info.requestId:String
pm.sendRequest:Function
pm.sendRequest
函数可以用来发送HTTP/HTTPS的异步请求,异步请求,只是在后台处理一些繁重的复杂的逻辑,或是发送多个请求,而不要等待阻塞下一个请求,你可以使用回调函数来处理返回结果。该函数可以用于前置脚本和测试脚本中。
// 普通URL pm.sendRequest('https://postman-echo.com/get', function (err, res) { if (err) { console.log(err); } else { pm.environment.set("variable_key", "new_value"); } }); // 构建SDK请求 const echoPostRequest = { url: 'https://postman-echo.com/post', method: 'POST', header: 'headername1:value1', body: { mode: 'raw', raw: JSON.stringify({ key: 'this is json' }) } }; 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
- Response Structure
pm.globals:VariableScope
pm.globals.has(variableName:String):function → Boolean
pm.globals.get(variableName:String):function → *
pm.globals.set(variableName:String, variableValue:String):function
pm.globals.unset(variableName:String):function
pm.globals.clear():function
pm.globals.toObject():function → Object
pm.environment:VariableScope
.pm.environment.has(variableName:String):function → Boolean
pm.environment.get(variableName:String):function → *
pm.environment.set(variableName:String, variableValue:String):function
pm.environment.unset(variableName:String):function
pm.environment.clear():function
pm.environment.toObject():function → Object
pm.variables:VariableScope
在Postman中,所有的变量定义都是有优先级的,在当前迭代请求中定义的变量优先于在环境中定义的变量,而环境变量优先于全局变量。优先级:Iteration Data
> Environment
>Global
,在环境中或全局定义的变量都可以通过下面函数获取到。
pm.variables.get(variableName:String):function → *
pm.request:Request
pm.request
对象包含请求信息,例如:
pm.request.url:
Url
pm.request.headers:
HeaderList
4.3 测试脚本
pm.response:Response
pm.response
对象包含响应信息,例如:
pm.response.code:Number
pm.response.reason():Function → String
pm.response.headers:
HeaderList
pm.response.responseTime:Number
pm.response.text():Function → String
pm.response.json():Function → Object
pm.iterationData:VariableScope
pm.iterationData
对象在使用Runner运行集合或文件夹时通过数据文件导入的数据。
pm.iterationData.get(variableName:String):function → *
pm.iterationData.toObject():function → Object
pm.cookies:CookieList
cookies
对象可以获取到Cookies信息,例如:
pm.cookies.has(cookieName:String):Function → Boolean
检查在当前的请求域名中是否存在特定的Cookiepm.cookies.get(cookieName:String):Function → String
获取特定Cookie的值pm.cookies.toObject:Function → Object
以对象形式获取 Cookies的值
pm.test(testName:String, specFunction:Function):Function
你可以使用该函数来编写测试点,来检验响应是否符合预期:
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'); });
pm.expect(assertion:*):Function → Assertion
pm.expect
是一个通用断言函数,使用ChaiJS expect BDD library标准,这个库可以让测试代码更具可读性。
pm.test('environment to be production', function () { pm.expect(pm.environment.get('env')).to.equal('production'); });
4.4 响应断言
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.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
:检验状态码是否为200pm.response.to.be.accepted
:检验状态码是否为202pm.response.to.be.badRequest
:检验状态码是否为400pm.response.to.be.unauthorized
:检验状态码是否为401pm.response.to.be.forbidden
:检验状态码是否为403pm.response.to.be.notFound
:检验状态码是否为404pm.response.to.be.rateLimited
:检验状态码是否为429
五、变量
Postman支持设置各种不同作用域的变量,可以在脚本中引用这些变量,也可以在请求的URL、参数、请求体等地方使用{{var_name}}
来调用,Postman可以在集合中设置变量,可以创建环境抽象来设置变量,也可以设置作用域最大的全局变量。简而言之,使用变量可以:
- 管理一些特殊的值,方便修改
- 根据不同的环境对同一变量设置不同的值
- 解析响应中的值,以及更灵活地构建一个请求
所有变量的值都只能是字符串类型,如果有复杂的对象类型,可以使用JSON字符串来设置。例如:
varstr=JSON.stringify(object);
varobj=JSON.parse(str);
1. 变量
1.1 变量作用域
Postman中有5种作用域:
- Global:全局变量
- Collection:集合变量
- Environment:环境变量
- Data:导入数据的变量
- Local/Tempoary:本地变量/临时变量
不同作用域有着不同的优先级,作用域的大小如下图所示,如果在两个作用域中有同名变量,取优先级大的作用域中的值,作用域越大,优先级越低。
1.2 在请求中使用变量
使用{{variableName}}
在构建请求时的任务地方插入变量的值,比如接口的域名、参数、请求头等地方。例如:
1.3 在脚本中使用变量
在脚本中通过代码获取在各个作用域中设置的变量:
- 使用
pm.environment.get()
和pm.environment.set()
来获取和设置环境变量 - 使用
pm.globals.get()
和pm.globals.set()
来获取和设置全局变量 - 使用
pm.variables.get()
可以从任何作用域中搜索值,使用pm.variables.set()
来设置变量
1.4 定义集合变量
在新建和编辑集合的时候,可以在Variables标签页里定义集合变量。
1.5 导入数据文件的变量
在使用 Runner 运行集合的时候,你可以导入CSV或者JSON数据文件,然后使用这些数据发送HTTP请求,这些数据的会根据字段名称导入为变量。这些变量在请求中一样可以使用{{variableName}}
来插入,在脚本中可以使用pm.iterationData.get("username")
来获取,例如:
1.6 内置变量
Postman提供了几个内置变量来加强请求,你可以在请求的URL、请求头或请求体等地方通过{{...}}
来使用,这些变量包括:
{{$guid}}
:生成字符串UUID{{$timestamp}}
:生成当前时间的时间戳{{$randomInt}}
:生成0到1000的随机整数
2. 环境和全局变量
当测试API的时候,你可能需要在本地环境、开发环境、测试环境或者生产环境使用不同的变量设置,使用环境变量可以很容易的实现这一功能。
2.1 创建环境和全局变量
点击界面右上方的 Manage Environments 图标来管理环境变量和全局变量。
2.2 选择环境
点击界面右上方的下拉列表框可以选择环境,选择不同的环境激活不同的变量组。
2.3 查看环境和全局变量
3. 作用域和优先级
下图描述了Postman中变量的工作方式:
变量使用示例:假设我们创建了三个集合C1、C2和C3,还创建了三个环境Dev、Staging和Prod,在三个环境中都存在一个URL变量,在不同的环境有不同的值。每个集合中有一个定义超时的变量,集合C1中的是10,C2中的是20,C3中的是30。定义一个全局变量重试次数为3,如下图所示。
现在,选择不同的环境就可以使用不同的URL,每个集合下的超时时间是不一样的,但与哪个环境无关,全局变量不管在哪个环境,哪个集合下都是一样的。