Make logging and tests results easy with custom pm functions

Go from this… Pre-Request Script

// Clear env vars at start of run
names = [ "access_token", "refresh_token", "oid_token", "resumeURL", "loginURL", "auth_code", "opentoken" ];
for (index in names) { if (obj.hasOwnProperty(index)) { pm.environment.set(names[index],"") }};
pm.test(pm.info.requestName + "Environment variables cleared", true);
console.info(pm.info.requestName + "Environment variables cleared");

and these Tests

console.info(pm.info.requestName + "OAuth login test started")

if (pm.response.status == "Found") {
    if (pm.response.headers.has("Location")) {
        pm.environment.set("resumeURL",pm.response.headers.get("Location"));

        pm.test(pm.info.requestName + "Successful initialisation", true);
        console.info(pm.info.requestName + "Successful initialisation");
    } else {
        pm.test(pm.info.requestName + "Request Failed: No Location Header", false);
        console.info(pm.info.requestName + "Request Failed: No Location Header");
        postman.setNextRequest(null);
    }
} else if (pm.response.status == "Service Unavailable") {
        pm.test(pm.info.requestName + "Service Unavailable: Is your config deployed?", false);
        console.info(pm.info.requestName + "Service Unavailable: Is your config deployed?");
        postman.setNextRequest(null);
} else {
        pm.test(pm.info.requestName + "Request Failed: No Redirect Found", false);
        console.info(pm.info.requestName + "Request Failed: No Redirect Found");
        postman.setNextRequest(null);
}

To this…

// Clear env vars at start of run
names = [ "access_token", "refresh_token", "oid_token", "resumeURL", "loginURL", "auth_code", "opentoken" ];
for (index in names) { if (obj.hasOwnProperty(index)) { pm.environment.set(names[index],"") }};
pm.pass("Environment variables cleared")

and this much easier to read and clean code

pm.log("OAuth login test started")

if (pm.response.status == "Found") {
    if (pm.response.headers.has("Location")) {
        pm.environment.set("resumeURL",pm.response.headers.get("Location"));
        pm.pass("Successful initialisation");
    } else {
        pm.fail(postman, "Request Failed: No Location Header");
    }
} else if (pm.response.status == "Service Unavailable") {
    pm.fail(postman, "Service Unavailable: Is your config deployed?");
} else {
    pm.fail(postman, "Request Failed: No Redirect Found");
}

And get test results like this…


And corresponding console logs like this…

All done with these custom pm functions in the Collection level pre-requests

// Custom PM Commands
// pm.fail
Object.prototype.fail = (postman, msg) => {
    pm.test(pm.info.requestName, () => { throw new Error(msg) });
    console.info(pm.info.requestName + " | " + msg);
    postman.setNextRequest(null);
};
// pm.pass
Object.prototype.pass = (msg) => {
    pm.test(pm.info.requestName + " | " + msg , true);
    console.info(pm.info.requestName + " | " + msg);
};
// pm.log
Object.prototype.log = (msg) => {
    console.info(pm.info.requestName + " | " + msg);
};
// Run collection once to load into Global Env space

Enjoy

5 Likes

What a clever way to customize your test logic, and still keep it abstracted and tidy! And a great example for people who want to halt the collection execution upon test failure :heart_eyes_cat:

Sadly, my brilliant idea was doomed from the start. The setNextRequest(null) inside the pm.fail is not having the effect I expected it to have during a collection run. It is to do with variable scope I am sure but documentation is scarce. Frustrating as I was so sure I had it. I am sure the real postman, whoever they are, would immediately know whats wrong. Until the clouds part and they arrive I shall flounder onward in the darkness that is my personal hades. Postman hell awaits me! :laughing:

1 Like

Hopefully I can give a nudge in the right direction :wink:

When I tried to write a Slack command handler in Postman, I used Try/Catch in addition to setNextRequest(null). When I needed to end the execution, I just threw the error that I wanted to log such as “Request Failed: No Location Header”.

Perhaps wrap your tests in a try block and then instead of calling pm.fail(message), throw the message and call pm.fail(message) in the catch block to pass the message to your logger. The throw will end the execution and still give you clean looking code :smile:

Like this?

Object.prototype.fail = (msg) => {
    pm.test(pm.info.requestName, () => { throw new Error(msg) });
    console.info(pm.info.requestName + " | " + msg);
    try:
       throw new Error(msg)
    catch:
       postman.setNextRequest(null);
};

Didnt work :pensive:

Tried this in Collection pre-request instead

stop = false;
Object.prototype.fail = (msg) => {
    pm.test(pm.info.requestName, () => { throw new Error(msg) });
    console.info(pm.info.requestName + " | " + msg);
    // but this is not affecting it -- why?
    // undocumented scope limitations?
    stop = true;
};
Object.prototype.pass = (msg) => {
    pm.test(pm.info.requestName + " | " + msg , true);
    console.info(pm.info.requestName + " | " + msg);
};
Object.prototype.log = (msg) => {
    console.info(pm.info.requestName + " | " + msg);
};

Then in the folder Tests I have the command

if (stop) { postman.setNextRequest(null) }

Still doesn’t work. Comments indicate the issue?

You will need to add postman as an additional first argument for it to work:

Object.prototype.fail = (postman, msg) => {
    pm.test(pm.info.requestName, () => { throw new Error(msg) });
    console.info(pm.info.requestName + " | " + msg);
    postman.setNextRequest(null);
};

In the Request Tests:

pm.fail(postman, "Environment variables cleared")
1 Like

I knew someone would know! God I love this place. :heart:

Is there a way to include postman without putting it in the function def? Or could I use = (msg, postman=postman) =>
This way if postman is not specified it uses that by default?

1 Like

Why thank you! It was a nice way to handle multi-level logic which the pm.test system does not lend itself to.

Kevin, thanks for the hints. Love the name by the way (same as mine). Not a fan of the postman documentation and have contributed to updating it but I want to build proper documentation on the pm command so I need at way to list all the available methods and parameters as this is not provided by Postman. It’s really strange but I cannot find this information anywhere. If you can help that would be rather marvellous.

Kevins!

So as far as I know, the documentation page on the pm object should include everything.

I don’t know of any additional places to get information on this however.

If I display the array below with the above object prototypes I get some unexpected results.

// Collection variable  specified in the Postman UI
env-vars = [ "access_token", "refresh_token", "oid_token", "resumeURL", "loginURL", "auth_code", "opentoken" ]

// Test script
vars = eval(pm.variables.get("env-vars"))
for (item in vars) {
         console.log(vars[item])
}

// Console Output
access_token
refresh_token
oid_token
resumeURL
loginURL
auth_code
opentoken
[Function]
[Function]
[Function]
[Function]

I had to use _.isObject to check if vars[item] was a function or not. Can you explain why this is happening and how I might prevent this variable pollution? Should I be using class extends rather than object prototype?

What’s the eval doing in that context?

I have finally learned that the for … in loop has the gotcha in that it iterates over the entire prototype chain. So you need to add if (obj.hasOwnProperty(key)) to only get get the values from your object. So to make the loop work properly it should look like the following…

for (item in vars) {
         if (obj.hasOwnProperty(key)) {
                  console.log(vars[item])
         }
}

Ref - How to Loop Through a JSON Response in JavaScript - SitePoint

Is there a way to include this by default from the current request?

Can you specify a varname that will automatically pull this from the current execution environment…

So in some environments you languages you can @ for the current data