How to use Promises in Postman tests?

Viewed 6605

I need to use some async code in my Postman test.

As it's a complex scenario I've reproduced the scenario in a very simple test with the following code:

let promiseNumber = 0;

function resolvedPromise() {
    return new Promise((resolve, reject) => {
        pm.sendRequest('https://postman-echo.com/get', (err, res) => {
            if (err) {
                console.log(err);
                reject();
            } else {
                console.log(`Resolved promise ${++promiseNumber}`);
                resolve();
            }
        });
    });
}

resolvedPromise()
    .then(resolvedPromise)
    .then(resolvedPromise)
    .catch(err => console.log(err));

The expected result on the console would be:

Resolved promise 1
Resolved promise 2
Resolved promise 3

But instead I receive:

Resolved promise 1

Is there a way to make Promises or async code available at Postman?

2 Answers

UPDATE: The original solution used 2147483647 as timeout value, now it was refactored to use Number.MAX_SAFE_INTEGER as suggested in the comments.

I did some more tests and realize that it always stop working after I use pm.sendRequest. If I try to resolve the Promise it works.

Seems a known bug looking at this thread.

The workaround for it would be only leave an open timeout while processing the code. Just ensure all possible paths clear the timeout or the call will hang for 300000 years

// This timeout ensure that postman will not close the connection before completing async tasks.
//  - it must be cleared once all tasks are completed or it will hang
const interval = setTimeout(() => {}, Number.MAX_SAFE_INTEGER);

let promiseNumber = 0;

function resolvedPromise() {
    return new Promise((resolve, reject) => {
        pm.sendRequest('https://postman-echo.com/get', (err, res) => {
            if (err) {
                console.log(err);
                reject();
            } else {
                console.log(`Resolved promise ${++promiseNumber}`);
                resolve();
            }
        });
    });
}

resolvedPromise()
    .then(resolvedPromise)
    .then(resolvedPromise)
    .then(() => clearTimeout(interval))
    .catch(err => {
        console.log(err);
        clearTimeout(interval);
    });

Now it prints the expected result:

Resolved promise 1
Resolved promise 2
Resolved promise 3

In addition to Felipe's answer, I'd like to share a little bit more with my experience in using Postman.

As I need to extract some values from the responses of pm.sendRequest and use them in making the main call (e.g. query string) and/or in the Tests section, I run the script in the Pre-request Script section and set the values in Postman environment variables.

One important point that I find out is that I must put all the code of setting variables (e.g. pm.environment.set(k, v)) before clearTimeout(timeout). Otherwise, if I do it the other way round, the pm.environment.set(k, v) code will still be run but the value of the environment variable will not be updated.


Below is an example with Postman v8.5.1.

Main call

Expect to get TEST from the environment variables.

GET http://google.com/{{TEST}}

Pre-request Script

Expect to set the TEST environment variable of which the value comes from the results of multiple APIs. In this example, I just use the last value returned from Promise.all.

// make sure you do NOT use Number.MAX_SAFE_INTEGER !!
const timeout = setTimeout(() => {}, 100000);

const promise = () => {
    return new Promise((resolve, reject) => {
        console.log('Calling');
        pm.sendRequest('https://jsonplaceholder.typicode.com/todos/' + _.random(1, 100), (err, res) => {
            console.log('run');
            if (err) {
                reject();
            } else {
                resolve(res.json());
            }
        });
    });
}

Promise.all([
    promise(),
    promise(),
    promise(),
    promise(),
    promise(),
    promise(),
    promise(),
    promise(),
]).then(values => {
    console.log('All done');
    const exampleValue = values[values.length-1].id;
    console.log("Last ID: " + exampleValue);

    clearTimeout(timeout);

    // move this line before clearTimeout to fix TEST being undefined
    pm.environment.set("TEST", exampleValue);
});

Tests

Expect to print the TEST environment variable.

// you get undefined if pm.environment.set is run after clearTimeout
// you get correct value if pm.environment.set is run before clearTimeout
console.log(pm.variables.get("TEST"));

How to test

After copying the URL and all the scripts to Postman, open up Console and click Send. Take a look at the query string of the actual URL being called (i.e. GET http://google.com/%7B%7BTEST%7D%7D). Then rearrange the code as mentioned in the comments and click Send again. This time everything should work as expected.

Related