Failing test displays "Error: timeout of 2000ms exceeded" when using Sinon-Chai

Bradley Braithwaite picture Bradley Braithwaite · Jan 24, 2014 · Viewed 17.3k times · Source

I have the following route (express) for which I'm writing an integration test.

Here's the code:

var q = require("q"),
    request = require("request");

/*
    Example of service wrapper that makes HTTP request.
*/
function getProducts() {

    var deferred = q.defer();

    request.get({uri : "http://localhost/some-service" }, function (e, r, body) {
        deferred.resolve(JSON.parse(body));
    });

    return deferred.promise;
}

/*
    The route
*/
exports.getProducts = function (request, response) {
    getProducts()
        .then(function (data) {
            response.write(JSON.stringify(data));
            response.end();
        });
};

I want to test that all the components work together but with a fake HTTP response, so I am creating a stub for the request/http interactions.

I am using Chai, Sinon and Sinon-Chai and Mocha as the test runner.

Here's the test code:

var chai = require("chai"),
    should = chai.should(),
    sinon = require("sinon"),
    sinonChai = require("sinon-chai"),
    route = require("../routes"),
    request = require("request");

chai.use(sinonChai);

describe("product service", function () {
    before(function(done){
        sinon
        .stub(request, "get")
        // change the text of product name to cause test failure.
        .yields(null, null, JSON.stringify({ products: [{ name : "product name" }] }));
        done();
    });

    after(function(done){
        request.get.restore();
        done();
    });

    it("should call product route and return expected resonse", function (done) {

        var writeSpy = {},
            response = {
            write : function () { 
                writeSpy.should.have.been.calledWith("{\"products\":[{\"name\":\"product name\"}]}");
                done();
            }
        };

        writeSpy = sinon.spy(response, "write");

        route.getProducts(null, response);
    });
}); 

If the argument written to the response (response.write) matches the test passes ok. The issue is that when the test fails the failure message is:

"Error: timeout of 2000ms exceeded"

I've referenced this answer, however it doesn't resolve the problem.

How can I get this code to display the correct test name and the reason for failure?

NB A secondary question may be, could the way the response object is being asserted be improved upon?

Answer

Louis picture Louis · Jan 24, 2014

The problem looks like an exception is getting swallowed somewhere. The first thing that comes to my mind is adding done at the end of your promise chain:

exports.getProducts = function (request, response) {
    getProducts()
        .then(function (data) {
            response.write(JSON.stringify(data));
            response.end();
        })
        .done(); /// <<< Add this!
};

It is typically the case when working with promises that you want to end your chain by calling a method like this. Some implementations call it done, some call it end.

How can I get this code to display the correct test name and the reason for failure?

If Mocha never sees the exception, there is nothing it can do to give you a nice error message. One way to diagnose a possible swallowed exception is to add a try... catch block around the offending code and dump something to the console.