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();
}
});
});
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);
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);
});