Search code examples
postman

How to store functions in Postman Collection Variables


I have some duplicate code I want to refactor and store in a postman collection variable. This function code is only duplicated once in my postman unit tests but I don't want even one duplication. So, I'm trying to store it in a collection variable and execute it however many times I need it.

As an arrow-function it works perfectly.

const parseMillisecondsIntoReadableTime = milliseconds => {
    // From the milliseconds parm, this function calculates 
    // hours (h), minutes (m) and seconds (s) and returns a string like:  "0h 29m 59s".
    return parseInt(h) + 'h ' + parseInt(m) + 'm ' + parseInt(s) + 's';
};

Now I want to store this function in a postman collection variable.

Object.prototype.parseMillisecondsIntoReadableTime = function(milliseconds) {
    // same code as above
    // From milliseconds parm, this calculates 
    // hours (h), minutes (m) and seconds (s) and returns this string.
    return parseInt(h) + 'h ' + parseInt(m) + 'm ' + parseInt(s) + 's';
};

This usage works perfectly.

let obj = {};
const timeToExpire = obj.parseMillisecondsIntoReadableTime(pm.collectionVariables.get('access_token_expiry')-new Date());
console.log('access_token valid for: ' + timeToExpire);

But, on unit tests somehow Postman references the function name in this error:

Response schema validation successful | Error: schema is invalid: data.properties['metadata'].properties['parseMillisecondsIntoReadableTime'] should be object,boolean.

The response object does have a schema with data and metadata properties. That's ok. How this error is occurring is perplexing.

This is the failing test.

    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;
    });

This is the successSchema layout.

Heading

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));

Final note: All my unit tests (322) worked until I tried to use a stored function in the collection variable.


Solution

  • One of the better ways to setup a re-usable function is to use a global variable in a pre-request script.

    For example (the function itself is not important, its just an example). You can put this at the request, folder or collection level.

    utils = {
        parseMillisecondsIntoReadableTime: function (milliseconds) {
            var seconds = Math.floor((milliseconds / 1000) % 60),
                minutes = Math.floor((milliseconds / (1000 * 60)) % 60),
                hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24);
    
            hours = (hours < 10) ? "0" + hours : hours;
            minutes = (minutes < 10) ? "0" + minutes : minutes;
            seconds = (seconds < 10) ? "0" + seconds : seconds;
    
            return hours + ":" + minutes + ":" + seconds;
        }
    };
    

    This is preferred to using EVAL which is not recommended, or by updating the prototype chain.

    You can then call the method using.

    var currentTime = +new Date(); // in milliseconds
    
    console.log(currentTime);
    
    console.log(utils.parseMillisecondsIntoReadableTime(currentTime));
    

    With the resulting console logs.

    enter image description here

    If you want to use Postman functions, then you need to pass the pm method like following.

    utils = {
        statusCode: function (pm, code) {
            pm.test(`Status code is ${code}`, () => {
                pm.response.to.have.status(code);
            })
            return utils;
        },
        response: function (pm) {
            console.log(pm.response.json());
            return utils;
        }
    };
    

    Which you can then call using..

    utils.response(pm);
    utils.statusCode(pm, 200);