javascriptunit-testingmocha.jsshould.js

should.js not causing mocha test to fail


I'm very new to unit tests, mocha, and should.js, and I'm trying to write a test for an asynchronous method that returns a promise. Here is my test code:

var should = require("should"),
    tideRetriever = require("../tide-retriever"),
    moment = require("moment"),
    timeFormat = "YYYY-MM-DD-HH:mm:ss",
    from = moment("2013-03-06T00:00:00", timeFormat),
    to = moment("2013-03-12T23:59:00", timeFormat),
    expectedCount = 300;

describe("tide retriever", function() {
    it("should retrieve and parse tide CSV data", function() {
        tideRetriever.get(from, to).then(
            function(entries) { // resolve
                entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
            },
            function(err) { // reject
                should.fail("Promise rejected", err);
            }
        );
    });
});

When I manually test the tideRetriever.get method, it consistently resolves an array of 27 elements (as expected), but the test will not fail regardless of the value of expectedCount. Here is my simple manual test:

tideRetriever.get(from, to).then(
    function(entries) {
        console.log(entries, entries.length);
    },
    function(err) {
        console.log("Promise rejected", err);
    }
);

I can also post the source for the module being tested if it's necessary.

Am I misunderstanding something about Mocha or should.js? Any help would be greatly appreciated.


Solution

  • UPDATE

    At some point Mocha started to support returning Promise from test instead of adding done() callbacks. Original answer still works, but test looks much cleaner with this approach:

    it("should retrieve and parse tide CSV data", function() {
        return tideRetriever.get(from, to).then(
            function(entries) {
                entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
            }
        );
    });
    

    Check out this gist for complete example.

    ORIGINAL

    CAUTION. Accepted answer works only with normal asynchronous code, not with Promises (which author uses).

    Difference is that exceptions thrown from Promise callbacks can't be caught by application (in our case Mocha) and therefore test will fail by timeout and not by an actual assertion. The assertion can be logged or not depending on Promise implementation. See more information about this at when documentation.

    To properly handle this with Promises you should pass err object to the done() callback instead of throwing it. You can do it by using Promise.catch() method (not in onRejection() callback of Promise.then(), because it doesn't catch exceptions from onFulfilment() callback of the same method). See example below:

    describe("tide retriever", function() {
        it("should retrieve and parse tide CSV data", function(done) {
            tideRetriever.get(from, to).then(
                function(entries) { // resolve
                    entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
                    done(); // test passes
                },
                function(err) { // reject
                    done(err); // Promise rejected
                }
            ).catch(function (err) {
                done(err); // should throwed assertion
            });
        });
    });
    

    PS done() callback is used in three places to cover all possible cases. However onRejection() callback can be completely removed if you don't need any special logic inside it. Promise.catch() will handle rejections also in this case.