如何在Postman集合变量中存储函数并解决校验报错
我希望重构重复代码并将其存储到Postman集合变量中,即使仅重复一次也想消除冗余。箭头函数形式的parseMillisecondsIntoReadableTime可正常运行,将其挂载到Object.prototype后调用也正常,但执行单元测试时出现schema校验错误:
Response schema validation successful | Error: schema is invalid: data.properties['metadata'].properties['parseMillisecondsIntoReadableTime'] should be object,boolean
此前322个单元测试全部正常,仅在尝试使用该存储函数后出现问题。
箭头函数实现
const parseMillisecondsIntoReadableTime = milliseconds => { // 从传入的毫秒数计算小时(h)、分钟(m)、秒(s),返回类似"0h 29m 59s"的字符串 const h = milliseconds / (1000 * 60 * 60); const m = (milliseconds % (1000 * 60 * 60)) / (1000 * 60); const s = (milliseconds % (1000 * 60)) / 1000; return parseInt(h) + 'h ' + parseInt(m) + 'm ' + parseInt(s) + 's'; };
挂载到Object原型的实现
Object.prototype.parseMillisecondsIntoReadableTime = function(milliseconds) { // 同上述箭头函数逻辑 const h = milliseconds / (1000 * 60 * 60); const m = (milliseconds % (1000 * 60 * 60)) / (1000 * 60); const s = (milliseconds % (1000 * 60)) / 1000; return parseInt(h) + 'h ' + parseInt(m) + 'm ' + parseInt(s) + 's'; };
正常调用代码
let obj = {}; const timeToExpire = obj.parseMillisecondsIntoReadableTime(pm.collectionVariables.get('access_token_expiry') - new Date()); console.log('access_token valid for: ' + timeToExpire);
失败的测试代码
pm.test("Response schema validation successful", () => { const responseData = pm.response.json(); const successSchema = JSON.parse(pm.collectionVariables.get("successSchema")); pm.expect(require("ajv")().validate(successSchema, responseData)).to.be.true; });
SuccessSchema结构
const successSchema = { type: "object", properties: { metadata: { type: "object", properties: { status: { type: "string" } }, required: ["status"] }, data: { type: "object", properties: { brandName: { type: "string" }, goodsSupplierNumber: { type: "string" }, supplierNumber: { type: "string" } }, required: ["brandName", "goodsSupplierNumber", "supplierNumber"] }, errors: { type: "array", items: { type: "object", properties: { code: { type: "string" }, message: { type: "string" } }, required: ["code", "message"] } } }, required: ["metadata", "data", "errors"] }; pm.collectionVariables.set("successSchema", JSON.stringify(successSchema));
问题原因
给Object.prototype添加自定义方法后,AJV默认会遍历对象的所有属性(包括原型链上的属性)。当校验接口返回的metadata对象时,AJV会把原型链上的parseMillisecondsIntoReadableTime方法当成metadata的属性,但你的Schema中metadata只定义了status字段,因此触发校验错误。
解决方案
方案1:避免修改Object原型,直接将函数存入集合变量
不要污染全局原型,把函数以字符串形式存储到集合变量,调用时通过Function构造函数实例化:
// 存储函数到集合变量(可在集合前置脚本中执行) pm.collectionVariables.set("parseMillisecondsIntoReadableTime", ` function(milliseconds) { const h = milliseconds / (1000 * 60 * 60); const m = (milliseconds % (1000 * 60 * 60)) / (1000 * 60); const s = (milliseconds % (1000 * 60)) / 1000; return parseInt(h) + 'h ' + parseInt(m) + 'm ' + parseInt(s) + 's'; } `); // 调用函数时 const parseFunc = new Function(`return ${pm.collectionVariables.get('parseMillisecondsIntoReadableTime')}`)(); const timeToExpire = parseFunc(pm.collectionVariables.get('access_token_expiry') - new Date()); console.log('access_token valid for: ' + timeToExpire);
方案2:修改AJV配置,仅校验对象自身属性
如果坚持使用原型方法,可以在初始化AJV时开启ownProperties选项,让AJV只校验对象自身的属性,忽略原型链上的属性:
pm.test("Response schema validation successful", () => { const responseData = pm.response.json(); const successSchema = JSON.parse(pm.collectionVariables.get("successSchema")); // 配置AJV仅校验对象自身属性 const ajv = require("ajv")({ ownProperties: true }); pm.expect(ajv.validate(successSchema, responseData)).to.be.true; });
方案3:使用无原型对象(局限性较大)
如果接口返回的metadata是你自己构造的,可以用Object.create(null)创建无原型的对象,这样原型链上的方法不会被遍历到,但此方案仅适用于你能控制responseData结构的场景,对接口返回的原始数据无效。
内容的提问来源于stack exchange,提问作者Davidson

