bguiz bguiz -4 years ago 190
Javascript Question

Using mocha+chai together with co

When

chai.expect
assertions fail,
they normally fail the test
and the negative result gets added to the report
for the test runner (in this case
mocha
).

However, when I use a generator function wrapped using
co.wrap()
,
as seen below,
something strange happens:
when the assertions pass, everything runs just fine.
When the assertions fail, however, the test times out.

How can
co
be used together with
mocha
+
chai
?




it('calls API and then verifies database contents', function(done) {
var input = {
id: 'foo',
number: 123,
};
request
.post('/foo')
.send(input)
.expect(201)
.expect({
id: input.id,
number: input.number,
})
.end(function(err) {
if (!!err) {
return done(err);
}

// Now check that database contents are correct
co.wrap(function *() {
var dbFoo = yield foos.findOne({
id: input.id,
});
continueTest(dbFoo);
})();

function continueTest(dbFoo) {
//NOTE when these assertions fail, test times out
expect(dbFoo).to.have.property('id').to.equal(input.id);
expect(dbFoo).to.have.property('number').to.equal(input.number);
done();
}
});
});





Solution:

The problem arose due to
co.wrap()
swallowing the exception thrown by
expect()
, not allowing it bubble up to where it needed to for
mocha
to find it, as pointed out by @Bergi below.

The solution was to use
co()
instead of
co.wrap()
, and add
.catch()
and pass that the
done
callback, as seen below.

// Now check that database contents are correct
co(function *() {
var dbFoo = yield foos.findOne({
id: input.id,
});
continueTest(dbFoo);
}).catch(done);

Answer Source

co.wrap catches exceptions from the generator, and rejects the returned promise. It "swallows" the error that is thrown from the assertions in continueTest. Btw, instead of using .wrap and immediately calling it, you can just call co(…).

co(function*() {
    …
}).then(done, done); // fulfills with undefined or rejects with error

or

co(function*() {
    …
    done();
}).catch(done);

Btw, to use co properly you'd put all your asynchronous functions in a single generator:

it('calls API and then verifies database contents', function(done) {
  co(function*() {
    var input = {
      id: 'foo',
      number: 123,
    };
    yield request
      .post('/foo')
      .send(input)
      .expect(201)
      .expect({
        id: input.id,
        number: input.number,
      })
      .endAsync(); // assuming you've promisified it

    // Now check that database contents are correct
    var dbFoo = yield foos.findOne({
      id: input.id,
    });

    expect(dbFoo).to.have.property('id').to.equal(input.id);
    expect(dbFoo).to.have.property('number').to.equal(input.number);

  }).then(done, done);
});
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download