postman | 接口测试 | tests 断言应用

1,694 阅读4分钟

tests 模块介绍

tests 功能即断言验证结果。没有断言的测试是没有灵魂的。postman 的 tests 模块运行是基于 node.js 的,所以在 tests 模块下编写的代码也是使用 js 的方式编写,postman 断言的方式是通过基于 chai 的单元测试框架,可以确保您的 API 能够按预期运行,确定服务之间的集成可靠运行,并验证新开发未破坏任何现有功能。

tests 中的断言发生在请求之后,与 pre-request script 中的功能恰恰相反。当我们点击 send 之后,request 得到响应才会去执行 tests 模块的代码。

tests 功能应用

tests 执行顺序

在 postman 中,断言可以写在 collection ,folder,或者 request 里面。

image.png 针对整个 collection 里面的请求,那么请求的顺序如下:

  • 先执行 collection 里面的 pre-request Script
  • 再执行 folder 里面的 pre-request Script
  • 最后执行 request 里面的 pre-request Script
  • 请求完成之后,先执行 collection 里面的 test
  • 再执行 folder 里面的 test
  • 最终执行 request 里面的 test

验证结果查看

image.png 我们通过在 tests 模块下编写的断言,运行之后就可以在 test result 模块下查看断言的结果。 如果断言失败,也可以在 consle log 下查看失败的原因。

tests 断言规范

这里说明一下,在过去的版本中,可能还有很多小伙伴使用的断言方式是这样的,例如断言一个响应体里面是否包含 user_id

tests["Body contains user_id"] = responsebody.has("user_id");

在新版本中,postman 官方已经不推荐使用这种方式断言。

image.png 新的断言方式应该是这样的,在 pm.test 的函数下包含 pm.expect 的断言:

pm.test("Status code is 200", () => {
  pm.expect(pm.response.code).to.eql(200);
});

当然,这是官方不推荐,并不是说官方停止。目前来说,两种方式的断言还是可以用的。

tests 断言语法

对于 http/https 请求来说,现在很多公司都是使用 json 作为响应体,postman 不仅仅支持 json 的断言,还有包括 xml,html,csv 都可以进行断言,所有格式都会解析成 postman 认识的 js 内容。

# 响应体为 json
const responseJson = pm.response.json();

# 响应体为 xml
const responseJson = xml2Json(pm.response.text());

# 响应体为 html
const $ = cheerio.load(pm.response.text());
//output the html for testing
console.log($.html());

# 响应体为 csv
const parse = require('csv-parse/lib/sync');
const responseJson = parse(pm.response.text());

  • 断言状态码
pm.test("Status code is 201", () => {
  pm.response.to.have.status(201);
});

如果您想测试状态代码是否是一组中的一个,请将它们全部包含在一个数组中并使用oneOf

pm.test("Successful POST request", () => {
  pm.expect(pm.response.code).to.be.oneOf([201,202]);
});
  • 断言请求体
pm.test("Content-Type header is present", () => {
  pm.response.to.have.header("Content-Type");
});

测试具有特定值的响应标头:

pm.test("Content-Type header is application/json", () => {
  pm.expect(pm.response.headers.get('Content-Type')).to.eql('application/json');
});
  • 断言响应体
pm.test("Person is Jane", () => {
  const responseJson = pm.response.json();
  pm.expect(responseJson.name).to.eql("Jane");
  pm.expect(responseJson.age).to.eql(23);
});
  • 断言响应时间
pm.test("Response time is less than 200ms", () => {
  pm.expect(pm.response.responseTime).to.be.below(200);
});

上面举部分例子说明断言的用法,具体更多断言语法可以查看 Chai 断言库文档

常见断言事例

  • 断言响应值的类型

根据响应体返回的 value 的类型进行断言,检查返回的类型是否符合我们的期待类型。当然这部分需要跟 api 文档描述的一致。

/* response has this structure:
{
  "name": "Postman",
  "age": 18,
  "hobbies": [
    "skating",
    "painting"
  ],
  "email": null
}
*/
const jsonData = pm.response.json();
pm.test("Test data type of the response", () => {
  pm.expect(jsonData).to.be.an("object");
  pm.expect(jsonData.name).to.be.a("string");
  pm.expect(jsonData.age).to.be.a("number");
  pm.expect(jsonData.hobbies).to.be.an("array");
  pm.expect(jsonData.website).to.be.undefined;
  pm.expect(jsonData.email).to.be.null;
});
  • 断言数组属性

检查数组是否为空,以及它是否包含特定值:

/*
response has this structure:
{
  "errors": [],
  "areas": [ "goods", "services" ],
  "settings": [
    {
      "type": "notification",
      "detail": [ "email", "sms" ]
    },
    {
      "type": "visual",
      "detail": [ "light", "large" ]
    }
  ]
}
*/

const jsonData = pm.response.json();
pm.test("Test array properties", () => {
    //errors array is empty
  pm.expect(jsonData.errors).to.be.empty;
    //areas includes "goods"
  pm.expect(jsonData.areas).to.include("goods");
    //get the notification settings object
  const notificationSettings = jsonData.settings.find
      (m => m.type === "notification");
  pm.expect(notificationSettings)
    .to.be.an("object", "Could not find the setting");
    //detail array must include "sms"
  pm.expect(notificationSettings.detail).to.include("sms");
    //detail array must include all listed
  pm.expect(notificationSettings.detail)
    .to.have.members(["email", "sms"]);
});
  • 断言对象属性
pm.expect({a: 1, b: 2}).to.have.all.keys('a', 'b');
pm.expect({a: 1, b: 2}).to.have.any.keys('a', 'b');
pm.expect({a: 1, b: 2}).to.not.have.any.keys('c', 'd');
pm.expect({a: 1}).to.have.property('a');
pm.expect({a: 1, b: 2}).to.be.an('object')
  .that.has.all.keys('a', 'b');
  • 断言一个值在一个集合中
pm.test("Value is in valid list", () => {
  pm.expect(pm.response.json().type)
    .to.be.oneOf(["Subscriber", "Customer", "User"]);
});
  • 断言包含一个对象
/*
response has the following structure:
{
  "id": "d8893057-3e91-4cdd-a36f-a0af460b6373",
  "created": true,
  "errors": []
}
*/

pm.test("Object is contained", () => {
  const expectedObject = {
    "created": true,
    "errors": []
  };
  pm.expect(pm.response.json()).to.deep.include(expectedObject);
});

验证响应结构

很多小伙伴应该遇到过一种情况,就是我希望可以验证响应的结构是否符合我期待的结构,而不是响应体里面的值。

postman 提供了两种方式对 json 的结构体进行验证

  1. 使用 Tiny Validator V4 (tv4) 执行 JSON 模式验证:
const schema = {
 "items": {
 "type": "boolean"
 }
};
const data1 = [true, false];
const 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;
});
  1. 使用 Ajv JSON 模式验证器验证 JSON 模式:
const schema = {
  "properties": {
    "alpha": {
      "type": "boolean"
    }
  }
};
pm.test('Schema is valid', function() {
  pm.response.to.have.jsonSchema(schema);
});

test setNextrequest()

可能在使用 postman 的过程中,有些小伙伴比较疑惑,如果我写了很多 request 了,但是在执行 collection 的时候,执行的顺序并不是我期待的那种,这样应该怎么解决呢? 在 postman 中,我们可以在 tests 模块下,编写我们需要执行下一步 request 的方法,告诉 postman 我们下一步应该执行哪一个接口。

postman.setNextRequest("request_name");

image.png

这样在执行完当前的接口时,postman 会执行下一个接口。request_name 指的是我们保存下来的 request 的名称。

总结

好了,这篇文章的内容就到这里了。希望可以帮助到大家对 postman 有更多的理解。