2
votes

I am trying to write some tests in Postman (I am running the Postman Jetpacks packaged app, if it does matter) and I am facing some inconsistencies.

The scope of my test is to verify the size (width and height) of a set of images against some predefined values.

The scenario is like this: I make a call to a method that returns some urls, then I set the URLs as environment variables and then I verifying the size of each picture. Below is the code I am using in Tests tab in Postman.

tests["Status code is 200"] = responseCode.code === 200;

var data = JSON.parse(responseBody);

//test that response contains the expected attributes
tests["splash_image_url present"] = data.hasOwnProperty("splash_image_url");
tests["home_image_url present"] = data.hasOwnProperty("home_image_url");
tests["login_image_url present"] = data.hasOwnProperty("login_image_url");
tests["register_image_url present"] = data.hasOwnProperty("register_image_url");
tests["splash_logo_url present"] = data.hasOwnProperty("splash_logo_url");
tests["bar_logo_url present"] = data.hasOwnProperty("bar_logo_url");

//set each image URL as environment variable
postman.setEnvironmentVariable("splash_image_url", data.splash_image_url);
postman.setEnvironmentVariable("home_image_url", data.home_image_url);
postman.setEnvironmentVariable("login_image_url", data.login_image_url);
postman.setEnvironmentVariable("register_image_url", data.register_image_url);
postman.setEnvironmentVariable("splash_logo_url", data.splash_logo_url);
postman.setEnvironmentVariable("bar_logo_url", data.bar_logo_url);


//extract images from each URL
var splash_image_url = document.createElement("img");
splash_image_url.src = environment.splash_image_url;
var home_image_url = document.createElement("img");
home_image_url.src = environment.home_image_url;
var login_image_url = document.createElement("img");
login_image_url.src = environment.login_image_url;
var register_image_url = document.createElement("img");
register_image_url.src = environment.register_image_url;
var splash_logo_url = document.createElement("img");
splash_logo_url.src = environment.splash_logo_url;
var bar_logo_url = document.createElement("img");
bar_logo_url.src = environment.bar_logo_url;

//test the size for each picture 
tests["splash_image_url width"] = splash_image_url.width === 640;
tests["splash_image_url height"] = splash_image_url.height === 960;

tests["home_image_url width"] = home_image_url.width === 640;
tests["home_image_url height"] = home_image_url.height === 960;

tests["login_image_url width"] = login_image_url.width === 640;
tests["login_image_url height"] = login_image_url.height === 960;

tests["register_image_url width"] = register_image_url.width === 640;
tests["register_image_url height"] = register_image_url.height === 960;

tests["splash_logo_url width"] = splash_logo_url.width === 310;
tests["splash_logo_url height"] = splash_logo_url.height === 80;

tests["bar_logo_url width"] = bar_logo_url.width === 155;
tests["bar_logo_url height"] = bar_logo_url.height === 40;

The problem is that sometimes when running the request all or some of the picture size verification fail. If I continue to manually run the same request again and again it will eventually show all the tests passed. This inconsistency makes the test unreliable.

I am missing something or doing something wrong? Is there a batter way to verify the picture size?

Thanks

1
Is it possible the tests are failing because you are getting a 304 - Not modified with an empty body instead of a 200? Try adding CacheControl: no-cache to the request - Darrel Miller
I found the following: - postman's send no-cache header option is set to on by default in settings - the response status as shown in postman is always 200 OK - checking the access logs server side also confirms that the status is 200 OK I guess we can rule this possibity out. Thanks for the suggestion anyway. - user4519133

1 Answers

1
votes

Very nice question, was a fun challenge. Thanks !

1. Your problem

The tests actually work after you manually run them a couple of times, because the images are cached at that point.

The main issue here, is that you are not waiting for images to actually load, before checking the properties associated with them.

2. Postman Test Results

I tried a proof of concept on this by waiting for the images to actually load and found out... that my tests were not actually being displayed either as passing or failing.

This was mainly due to the way tests are run. Postman uses eval in the context of the request (more or less).

in Evaluator.js @ 111

if (command === "runcode") {
    try {
        var result = eval(code);
        event.source.postMessage({'type': 'test_result', 'result': result, 'scriptType': scriptType}, event.origin);
    }
    catch(e) {
        console.log(e);
        event.source.postMessage({'type': 'test_error', 'errorMessage': e.message, 'scriptType': scriptType}, event.origin);
    }

}

Unfortunately for us, any sort of the callback logic will not get retroactively pushed back into the results chain.

2.1 Postman Secondary Test Results

It seems that posting a new set of results to event.source will not trigger a new set of results, but get completely discarded.

I have managed to find a workaround for this. Simply end the script with:

function postmanJetpacksSupportsOnlyOneResultPerTest()
{
    event.source.postMessage({'type': 'test_result', 'result': tests, 'scriptType': 'test'}, event.origin);
}

throw 'ignore this. Enforcing failure on this test case, real values will come  by manually calling *postmanJetpacksSupportsOnlyOneResultPerTest* when you are done with the test case.';

Then just call postmanJetpacksSupportsOnlyOneResultPerTest when all of your callbacks are done.

3. Postman Developers

I really hope you can somehow include the concept of promises. Example:

In the test runner:

var defered = new jQuery.Deferred();
tests['before timeout'] = true;
setTimeout(function() {
    tests['on timeout'] = true;
    defered.resolve(tests);
}, 500);
tests['after timeout'] = true;
return defered.promise();

In Evaluator.js @ 113:

var result = eval(code);
if (result.promise instanceof Function) {
    result.then(function(result) {
       event.source.postMessage({'type': 'test_result', 'result': result, 'scriptType': scriptType}, event.origin);
    });
} else {
    event.source.postMessage({'type': 'test_result', 'result': result, 'scriptType': scriptType}, event.origin);
}

4. An example

// handlers
var waiting = 0;
function errorHandler() {
    waiting--;
    tests[this.image.name + ' load'] = false;

}
function successHandler() {
    waiting--;
    tests[this.image.name + ' load'] = true;
    tests[this.image.name + ' width'] = this.image.width == this.width;
    tests[this.image.name + ' height'] = this.image.height == this.height;
}

// test case kind of
function createImageTest(name, url, width, height)
{
    // create image tag
    var image = document.createElement('img');
    // set the name
    image.name = name;
    // set error handlers
    image.onerror = errorHandler.bind({
        image: image
    });
    image.onload = successHandler.bind({
        image: image,
        width: width,
        height: height
    });
    // finally attach the src
    image.src = url;
    // waiting on either a fail or a load
    waiting++;
}

// the actual test cases
createImageTest('stackexchange logo', 'http://cdn.sstatic.net/stackexchange/img/se-logo.png', 223, 52);
createImageTest('stackoverflow logo', 'http://cdn.sstatic.net/stackoverflow/img/sprites.png', 240, 500);

// wait for all callbacks finished
(function checkFinished(){
    // still images to process
    if (waiting) {
        // check back in another 0.1 seconds
        return setTimeout(checkFinished, 100);
    }

    // ready to send result
    postmanJetpacksSupportsOnlyOneResultPerTest();
})();

// the hack from #2.1
function postmanJetpacksSupportsOnlyOneResultPerTest()
{
    event.source.postMessage({'type': 'test_result', 'result': tests, 'scriptType': 'test'}, event.origin);
}

throw 'ignore this. Enforcing failure on this test case, real values will come from init...';