Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mocha with nodejs assert hangs/timeouts for assert(false) instead of error

I have this kind of a mocha test:

describe 'sabah', →
    beforeEach →
        @sabahStrategy = _.filter(@strats, { name: 'sabah2' })[0]
            .strat

    it 'article list should be populated', (done) →
        @timeout 10000
        strat = new @sabahStrategy()
        articles = strat.getArticleStream('barlas')
        articles.take(2).toArray( (result)→
            _.each(result, (articleList) →

                // I make the assertions here
                // assert(false)
                assert(articleList.length > 1)
            )
            done()
        )

The problem is, whenever I do assert(false), the test hangs until the timeout, instead of giving an assertion error, why?

Edit:

For example if I have these two tests

    it 'assert false', (done) →
        assert(false)
        done()

    it 'article link stream should be populated', (done) →
        @timeout 20000
        articles = @sabahStrategy.articleLinkStream('barlas')
        articles.pull((err, result)→
            console.log('here')
            assert(false)
            console.log('after')
            assert(!err)
            assert(result.length > 1);
            _.each(result, (articleList) →
                assert(articleList.link)
            )
            done()
        )

The first one, gives the assertion error as expected, the second one, logs here, and hangs at assert(false) so after is never logged. It has something to do with articles being a stream and the assertion is within a pull callback, this is from the highland.js API.

Solved Edit:

So according to Paul I fixed the problem with this code:

    it 'article stream should be populated', (done) →
        @timeout 30000
        articles = @sabahStrategy.articleStream('barlas')

        articles.pull((err, result) →
            try
                # assert false properly throws now.
                assert(false)
                assert(!err)
                assert(result.length == 1)
                assert(result[0].body)
                assert(result[0].title || result[0].title2)
                done()
            catch e
                done(e)
        )

Edit2:

I've produced a simplified version of the problem:

h = require('highland')
Q = require('q')

describe 'testasynchigh', →
    beforeEach →
        @deferred = Q.defer()
        setTimeout((→
            @deferred.resolve(1)
        ).bind(this), 50)


    it 'should throw', (done) →
        s = h(@deferred.promise);
        s.pull((err, result) →
            console.log result
            assert false
            done()
        )

I see your version does indeed work @Louis, but if you involve promises into the mix, mocha can't handle the problem, so it will hang in this example. Also try commenting out the assert false and see it passes.

So Louis I hope I got your attention, could you explain the problem, and try catch looks ugly indeed and I hope you find a reasonable solution to this.

like image 494
user3995789 Avatar asked Nov 06 '14 01:11

user3995789


2 Answers

Because that's what you're telling it you want to do, when you add the 'done' callback.

The way to actually do this test is to call return done(err) if the assertion would fail, where err is any string or error object you want reported.

First, when your assertion fails, the program throws an exception and never reaches done(), which is why you're not seeing done get called. This is how assertions are supposed to work, however since you're in an async test, the result is that the callback never fires, which is why you hit the timeout.

Second, as my original answer said err is any error you want to send out from the test. It can be a string error message or a full-on Error object subclass. You create it and then pass it to done() to indicate the test failed.

The better way to structure your code in an asynchronous test is to use your tests as simple booleans, rather than as assertions. If you really want to use assert, then wrap it in a try..catch. Here's a couple examples:

if(err) return done(err); // in this case, err is defined as part of the parent callback signature that you have in your code already.

if(result.length < 1) return done('Result was empty!'); 

Finally, if you really want to assert, then you can:

try{
  assert(!err);
}catch(e){
  return done(e);
}

I'm calling return done(err) rather than done(err) because it stops the rest of the code from executing, which is normally what you want.

like image 82
Paul Avatar answered Oct 26 '22 02:10

Paul


For anyone with same issue: You should make sure that done() gets called even after an assert fails, like in the following code:

try {
  // your asserts go here
  done();
} catch (e) {
  done(e);
}
like image 31
thameera Avatar answered Oct 26 '22 03:10

thameera