一、使用Postman的模拟服务模拟(mock)后端
在整个开发过程中,前端或后端的延迟可能会阻碍相关团队有效地完成工作。一些后端的API工程师已经开始使用Postman去测试后端endpoint,而不依赖于前端UI来模拟API请求。
现在,Postman开发了一种新的模拟服务,使团队能够模拟后端服务器。前端开发人员可以模拟Postman collection(和相应的环境)中的每个endpoint,以查看潜在的响应,而无需实际启动后端。
前端,后端和API团队现在可以并行工作,从而释放之前因这些依赖性而延迟的开发人员。现在,让我们看看Postman是如何模拟后端的。
1、设置一个用于模拟的集合
在我的最强PostMan使用教程(2) - 在test suite中运行test case中已经介绍过了Collection集合,这里,前端开发得先知道有哪些url是用于和后端通信的。
在这个例子中,我们有一个Tesk Mock
的集合,并且配置了同名的环境的Tesk Mock
。
集合中,有两个request:mock_r_1
和mock_r_2
,一个GET,一个POST,对应的path分别是/test,/tp。
接下来,让我们设置一个 mock server 并且为每一个endpoint模拟一条response。注意,这两个是必须的步骤,没有必然的先后顺序。你可以先mock response,再创建mock server,也可以先创建mock server,再mock response。这里,我们先创建一个mock server。
2、创建mock server
进入左边的导航栏,选择我们的Test mock
集合,会出现一个三角符号(如下图中Postman Echo
集合旁边的符号),点击之后,三角符号会反向(如下图中Test mock
集合旁边的符号),并且出现针对该集合的配置。这里有一个Mocks,请选择之:
接下来的步骤很简单,一路点击创建即可:
最后一个步骤,我们得到了一个mock server的地址,也就是说,这个mock server是postman在自己的服务器上为我们创建的,无论我们是否打开postman,我们都可以直接用这个地址来访问mock的API。
别担心你会忘掉这个地址,因为它会自动保存在collection的配置中,按照我们刚才是的步骤,回到这个集合中,在Mocks下面,你会看到你创建的server:
点击图中的图标,会直接复制到粘贴板上。然后你可以将其保存在Tesk mock
环境的变量中。也可以直接使用。
但这时,你拥有的只是一个mock server,上面没有任何服务可用。你需要在这个server上注册endpoint,并且mock对应的response,才可以使之服务于你。
3、mock每个endpoint的response(创建example)
这里的步骤也不复杂,针对每个endpoint,或者说是host下面的path,你需要自己提供mock data。在postman里面,这个概念叫做example,即每一个你需要访问的路径/接口/资源,都可以提供一个栗子,即便你连不上,你也可以通过例子知道该路径/接口/资源大概会返回什么样的response。这个example不单单是为了mock而做的,因为postman可以生成API文档,在生成文档的时候,这个例子也会直接放在文档里,方便查阅文档的人了解接口。
具体创建步骤:
- 打开collection,选择request
- 为request创建一个example
- 保存example
这里需要注意的是:
- 每个request都可以有多个example
- 每个example创建之后都可以修改
- Mock server只会采用最后创建的example
4、使用mock server
在前面的步骤中,我们已经创建了mock server,并且为每个end point至少创建了一个example。这时,mock server就不再只是一个空壳了,它会为每一创建了example的endpoint提供mock服务,mock的内容就是最后创建的example的内容。
我们可以直接在postman上测试:
也可直接在浏览器上验证:
注意,在验证第二个endpoint的时候,失败的原因是,endpoint是POST权限的,但浏览器默认是用GET去访问。
5、对开发模式的启发
使用postman,前端团队和后端团队完全可以在约定了API接口之后,各自完成开发,而不需要收到对方进度的影响,因此在真正开始开发工作之前,接口、数据格式的约定变得尤为重要,当然,因为postman是支持组开发的,即便接口,数据有变化,也可通过share collection的方式,及时同步collection下request的变化,并立即反应到mock server上。并且,因为mock server是挂载postman的服务器上,只要有网络,前端是可以在家办公,而不用受制于内网限制的。
二、使用Postman生成接口文档和示例
1、流程概要
定义一套接口,为接口生成文档和示例一般来说有以下步骤:(这里只列出postman参与的部分)
- 创建一个接口集合,描述该接口集合的功能、范围等信息
- 将所有需求已清晰的接口定义到集合当中
- 创建接口(add request),描述该接口的作用
- 为该接口上的所有参数增加描述信息(params, header, body)
- 为接口生成样例
- 成功样例
- 失败样例
- 异常样例
- 重复前两个步骤,完成接口集中的接口定义
- 创建文档
接下来,我们以elasticsearch的Restful API接口作为一个示例,来展示如何用Postman生成接口文档和示例
2、创建一个接口集合
假设,我们要开发elasticsearch的增删改查服务,并且已经在团队内对接口达成了共识,并将为其创建可维护的文档。
3、创建接口(add request),描述该接口的作用
如上文提到,我们有4个接口,增,删,改,查,客队elasticsearch中的索引进行对应操作。首先,让我们添加一个create文档的接口。并添加对接口对简单描述:
4、为该接口上的所有参数增加描述信息(params, header, body)
填入接口的细节:
5、为接口生成样例
成功样例
在创建样例的时候,我们可以根据约定好的返回内容,填充到样例中(example)。
在接口已经开发好的情况下,我们还可以点击send
按钮,直接将接口的response保存为样例:
我们还可以创建一个稍微复杂的样例,比如列出接口上可用的参数:
失败样例
同样,我们可以保存一个失败的样例,比如,当date类型的值,被输入一个字符串的时候,接口将会报错。
异常样例
我们可以类比之前的两个样例,依葫芦画瓢,这里就不再重复。
6、创建并查看文档
在将接口都创建到集合之后,我们就可以创建文档了,具体的方法很简单,在集合的选项中,点击view in web
,即可看到创建的文档,以及文档中的示例:
7、发布文档
在接口经过数轮的修改,可以发布之后,我们也可以发布接口文档了。这里,postman团队背靠google是很有钱的,它提供了域名和服务器为我们存储在互联网上发布所有人都可以查看的文档:
8、根据接口示例创建mock服务
我在上一篇文章最强PostMan使用教程(4)- 使用Postman的模拟服务模拟(mock)后端 里面已经介绍了如何创建mock server。这个mock server仍然是postman团队为我们提供的域名和服务器,因为我们在创建接口文档的过程中,已经创建了各种样例,所以,我们是可以直接生成mock server,而不需要额外的工作的。具体请移步上文。
至此,我们就完成了整个使用Postman生成接口文档和示例的过程。
三、使用Postman的Pre-request Script自动生成通过网关需要校验的签名
1、Pre-request Script
在开始编写案例之前,先简单介绍一下Postman的Pre-request Script到底是个什么东西?从字面意思能猜测出这是一个在请求之前会执行的脚本,我们看下官方给出的流程图,就可以证明我们的猜想。
Pre-request Script是通过Postman的沙盒(Sandbox)来实现的,而Sandbox是一个JavaScript执行环境,里面内置了一部分常用的JS库和函数(下面会介绍到)。所以我们其实是通过编写JavaScript代码,代码在请求发出前先被执行,从而达到自动生成签名的目的。
2、实战
先切换到Pre-request Script
选项卡,开始编写JavaScript代码。
假设服务端的签名算法如下:所有请求参数都放在Body
里以json
格式提交,里面必须包含一个当前时间戳ts
以及签名后得到的sign
。对body中除了sign
以外的所有字段签名(暂时不考虑json有数组的情况),先按照key升序
排列,然后以key=value
的形式用&
拼接字符串,最后用签名密钥signKey
对拼接后的字符串进行SHA1
签名得到sign
。我们直接贴代码吧。
(function () { var data = JSON.parse(request.data); var signKey = pm.environment.get('signKey'); var sign = calcSign(data, signKey); pm.globals.set('sign', sign); })(); function calcSign(data, signKey) { delete data['sign']; var keys = []; for(var k in data) { keys.push(k); } keys.sort(); var kv = []; for (var v of keys) { kv.push(v + '=' + data[v]); } var kvStr = kv.join('&'); var sign = CryptoJS.HmacSHA1(kvStr, signKey).toString(); return sign; }
我们还需要在请求的Body
里加上ts
和sign
字段,而且必须用双引号括起来,否则会报错。还要在环境变量或全局变量(视情况而定)里设置signKey
字段,如图所示。
代码中使用到了Postman Sandbox的一些常用的内置函数,这里简单介绍一下。Sandbox内置的类库以及完整的Postman API介绍请看最下面的文档,这里无法一一罗列和演示。
(function(){ ... })()
:jQuery的写法,Sandbox内置了jQuery库。pm.environment.get(variableName)
:获取某个环境变量的值,也可以写成 postman.getEnvironmentVariable(variableName)。pm.globals.set(variableName, variableValue)
:设置某个全局变量,也可以写成 postman.setGlobalVariable(variableName, variableValue)。CryptoJS.HmacSHA1()
:内置的CryptoJS库,几乎涵盖所有加密算法,本案例使用了SHA1,大家根据具体签名规则来选择。{{$timestamp}}
:动态返回当前时间戳。类似的语法还有{{$guid}}
生成唯一ID,{{$randomInt}}
生成0到1000的随机整数。
如果每个请求都要加签名,不必为每个请求都写一份重复的Pre-request Script代码。可以在收藏
或者文件夹
级别统一设置,这样该收藏或文件夹下每一个请求都能执行到。
3、例二:
下面举个算法签名的例子,请求是GET方式,需要四个参数:timestamp、sign、token、userId。
这里设置了全局变量,通过{{name}}就可以取到值,可以在Pre-request Script通过代码postman.setGlobalVariable(name, value)设置。
1. 签名算法
签名算法很简单:将timestamp、userId这两个参数组合成字符串,对字符串进行MD5加密。将加密后的值作为sign参数的值传入接口就可以了。具体代码如下:
//全局变量:设置时间戳参数 postman.setGlobalVariable("timestamp", new Date().getTime().toString()); //get中的参数 var queryParam = pm.request.url.query.members; //数组参数转为map参数 var param ={}; for (var item in queryParam){ param[queryParam[item].key] = postman.getGlobalVariable(queryParam[item].key) == null ? queryParam[item].value : postman.getGlobalVariable(queryParam[item].key); } //移除参数sign delete param["sign"]; //param排序 paramSorted = objSort(param); console.log("paramSorted:==",paramSorted); //生成签名 var sign = ""; for (let key in paramSorted){ sign += key+"="+paramSorted[key]+"&"; } console.log("sign:==",sign); var md5Str =CryptoJS.MD5(sign); postman.setGlobalVariable("sign", md5Str.toString(CryptoJS.enc.Hex).toUpperCase()); //排序方法 function objSort(obj) { let keys = Object.keys(obj).sort(); let arr = {}; for (let i in keys) { arr[keys[i]] = obj[keys[i]]; } return arr; }
4、postman常用方法集合:
1.设置环境变量
pm.environment.get("key", "value");//postman 5.0以上版本设置环境变量的方法
2.设置全局变量
pm.globals.set("variable_key", "variable_value");//postman 5.0以上版本设置全局变量方法
3.检查response body中是否包含某个string
pm.test("Body is correct", function () { pm.response.to.have.body("response_body_string"); });//5.0以上版本方法
4.检测JSON中的某个值是否等于预期的值
var data = JSON.parse(responseBody); tests["Your test name"] = data.value === 100;
JSON.parse()方法,把json字符串转化为对象。parse()会进行json格式的检查是一个安全的函数。
如:检查json中某个数组元素的个数(这里检测programs的长度)
var data = JSON.parse(responseBody); tests["program's lenght"] = data.programs.length === 5;
5.转换XML body为JSON对象
var jsonObject = xml2Json(responseBody); tests["Body is correct"] = responseBody === "response_body_string";
6.检查response body是否与某个string相等
7.测试response Headers中的某个元素是否存在(如:Content-Type)
//getResponseHeader()方法会返回header的值,如果该值存在 tests["Content-Type is present"] = postman.getResponseHeader("Content-Type"); tests["Content-Type is present"] = responseHeaders.hasOwnProperty("Content-Type");
上面的方法,不区分大小写。下面的方法,要区分大小写。
8.验证Status code的值
pm.test("Status code is 200", function () { pm.response.to.have.status(200); });//5.0以上版本方法
9.验证Response time是否小于某个值
pm.test("Response time is less than 200ms", function () { pm.expect(pm.response.responseTime).to.be.below(200); });
10.name是否包含某个值
//5.0以上版本方法 pm.test("Status code name has string", function () { pm.response.to.have.status("Created"); });
11.POST 请求的状态响应码是否是某个值
//5.0以上版本方法 pm.test("Successful POST request", function () { pm.expect(pm.response.code).to.be.oneOf([201,202]); });
12.很小的JSON数据验证器
var schema = { "items": { "type": "boolean" } }; var data1 = [true, false]; var data2 = [true, 123]; console.log(tv4.error); tests["Valid Data1"] = tv4.validate(data1, schema); tests["Valid Data2"] = tv4.validate(data2, schema);
13.获取request值:
var Json = JSON.parse(request.data);
- data {object}:this is a dictionary of form data for the request. (request.data["key"]=="value")
- headers {object}:this is a dictionary of headers for the request (request.headers["key"]=="value")
- method {string}:GET/POST/PUT etc.
- url {string}:the url for the request.
假设requestBody中有"version":"1.0";这个值,如果想获取到version的value值,代码如下
var Json = JSON.parse(request.data); var version = Json["version"];
14.JSON.parse()和JSON.stringify()
JSON.parse()【从一个字符串中解析出json对象】--把string转对象
JSON.stringify()【从一个对象中解析出字符串,主要针对[object object]类型数据的转换】--把对象转String
var data={name:'goatling'} JSON.parse(data) 结果是: '{"name":"goatling"}' JSON.stringify(data) 结果是:name:"goatling"
15.判断字段值是否为空typeof()
var Jsondata = JSON.parse(responseBody); if( typeof(Jsondata.data) != "undefined" )