When testing async code with Mocha and one of my asserts fails, all Mocha does is to report a timeout error. Is there a way to improve this? How to know what asserts failed and why?
mocha
Contact
#getContacts()
1) should return at least 1 contact
0 passing (3s)
1 failing
1) Contact #getContacts() should return at least 1 contact:
Error: timeout of 3000ms exceeded. Ensure the done() callback is being called in this test.
Code:
var assert = require("assert");
var contact = require("../lib/contact.js");
var chai = require('chai');
var should = chai.should();
describe('Contact', function() {
describe('#getContacts()', function() {
it('should return at least 1 contact', function(done) {
contact.getContacts().then(function(contacts) {
assert.equal(4,2)
done()
});
})
})
});
The issue is that the assertion fails, which throws an exception. This causes the promise to be rejected, but there isn't anyone to notice. Your code only checks if the promise succeeds. If you return the promise, then mocha will check for it and fail the test if the promise is rejected.
So you want
it('should return at least 1 contact', function() {
return contact.getContacts().then(function(contacts) {
assert.equal(4,2);
});
});
You should return the promise like this:
it('should return at least 1 contact', function() {
return contact.getContacts().then(function(contacts) {
assert.equal(4,2);
});
});
It seems like when the assert throws an error that error is swallowed and never shown and also the code after assert throws is skipped.
Try like this (catching the reject):
it('should return at least 1 contact', function(done) {
contact.getContacts().then(function(contacts) {
assert.equal(4,2)
done()
}).then(null, function (err) {
console.error(err);
done(err);
});
})
Or instead of then(null, rejectFunc) use catch(rejectFunc) with libs like bluebird.
Also the answer by idbehold is great. I didn't know yet that mocha supports promises directly and I always use the done param knowing if I have a timeout without a stack trace there was a swallowed error in this test.
Related
Looking at other questions, can't really find the cause of the problem. I am trying to test using mocha.
it("Should not do the work",function(done) {
axios
.post("x/y",{ id:a2 })
.then(function(res) {
assert(false,"Should not do the work");
done();
})
.catch(function(res) {
assert.equal(HttpStatus.CONFLICT,res.status);
done();
});
});
it("Should do the work",function(done) {
axios
.post("/x/y",{ id: a1 })
.then(function(res) {
done();
})
.catch(done);
});
The result was:
√ Should not do the work (64ms)
1) Should do the work
1 passing (20s)
1 failing
1) Error: Timeout of 20000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
Increasing the timeout didn't work.
Don't forget you can simply return a promise in Mocha and it will deal with it accordingly. In your first example are you sure those blocks are actually executed?
Doing an assertion may cause an exception, which can sabotage what you're trying to do. If your promise library supports it you can always:
it("Should not do the work",function(done) {
axios.post("x/y",{ id:a2 })
.then(function(res) {
assert(false,"Should not do the work");
})
.catch(function(res) {
assert.equal(HttpStatus.CONFLICT,res.status);
})
.finally(done);
});
Ensuring that should be done regardless.
Even better:
it("Should not do the work",function() {
return axios.post("x/y",{ id:a2 })
.then(function(res) {
assert(false,"Should not do the work");
})
.catch(function(res) {
assert.equal(HttpStatus.CONFLICT,res.status);
})
});
Watch for doing catches on assertions and assertions in catches. A better plan might be async:
it("Should not do the work", async function() {
var res = await axios.post("x/y",{ id:a2 })
assert.equal(HttpStatus.CONFLICT,res.status);
});
I'm using Mocha and Should.js to test a promise that I am expecting to generate an error.
Because it is a promise, I don't believe I can simply use should.throwError(). This just means that I would like to fail the unit test in the .catch block of the promise.
How do I explicitly fail a unit test using without using some sort of stupid hack like 1.should.equal(2)?
Example Code (Which does not work)
it('should throw an error.', function(done) {
myPromiseGenerator().then(function() {
should.fail();
done();
}).catch(function(e) {
done();
})
}
Pass the error to the done function.
it('should throw an error.', function(done) {
myPromiseGenerator().then(function() {
done(new Error("should not succeed"));
}).catch(function(e) {
done();
})
}
When running tests that utilize domains for error handling, Mocha still appears to be throwing an error even if a domain handler inside a library should have caught the error. If I execute the code outside of Mocha, it functions correctly leading me to believe the problem is Mocha.
Example:
foo.js
module.exports = function(done) {
var domain = require("domain");
var d = domain.create();
d.on("error", function() {
done();
});
d.run(function() {
throw new Error("foo");
});
}
test.js - Error thrown inside foo.js is not being caught by the domain.
describe("test", function() {
it("should succeed", function(done) {
var foo = require("./foo.js");
foo(function() {
console.log("done");
done();
});
});
});
result : error thrown
script.js - error is being properly caught by the domain and bubbled up.
var foo = require("./foo.js");
foo(function() {
console.log("done");
});
result : done
As you can see above, if I node straight to script.js it functions as desired, the error is caught by the domain handler and the code continues. If I run the same block of code inside a Mocha test, the error halts the test and a failure is given. I believe this is because the error is being sent on an uncaughtException handler, or something like it. The other complication is that it works properly in Mocha, if I have a process.nextTick() around the function call, leading me to believe that Mocha is only unable to handle synchronous errors, but works just fine with async errors.
There is some talk of this problem here: https://groups.google.com/forum/#!msg/nodejs/n-W9BSfxCjI/SElI1DJ_6u0J and https://github.com/joyent/node/issues/4375 .
The confusion I have is that all of this discussion seems to state the problem has been resolved months ago. Anyone know of either a simple work-around for the issue, or a why I'm not seeing a bug fixed which other people seem to believe is fixed at this point in time.
I am running node v0.10.18 and Mocha 1.13.0 on CentOS 6.3 Vagrant VirtualBox on Windows 7.
Found the problem. NodeJS domains catch synchronous errors but the event continues to bubble to a try/catch. If you wrap a domain.run() in a try/catch then the domain error handler AND the catch will be executed.
Thus, it seems the best practice is to use process.nextTick inside all domain.run(). This is shown in the docs example, but isn't expressed as explicitly as I would prefer.
Example:
d.run(function() {
process.nextTick(function() {
// do stuff
});
});
In this case, the flaw is not in Mocha.
Proof of NodeJS domains not catching synchronous errors in try/catch: https://gist.github.com/owenallenaz/7141699
nodejs domains do definitely catch synchronous errors
See this simple test case
var domain = require("domain");
var d = domain.create();
d.on("error", function() {
console.log("domain caught");
});
d.run(function() {
throw new Error("foo");
});
// result: domain caught
EDIT: Since writing this answer I have written a blog post describing what is going on with domains and try catch and whether you can use domains as a wholesale replacement for try catch. It summarises most of what has been discussed here.
http://www.lighthouselogic.com/node-domains-as-a-replacement-for-try-catch/
ORIGINAL ANSWER:
Actually there is a problem with Mocha.
I wrote the following test function:
function error(callback){
var d = domain.create().on('error', function(err){
console.log("Caught Error in Domain Handler")
return callback(err);
});
d.enter();
throw new Error("TestError");
d.exit();
}
Then I wrote a simple test without mocha:
error(function(err){
if(err)
{
console.log("Error was returned");
}else
{
console.log("Error was not returned")
}
})
The output I received was:
Caught Error in Domain Handler
Error was returned
When I tested using Mocha:
describe('Domain Tests', function(){
it('Should return an error when testing', function(done){
error(function(err){
if(err)
{
console.log("Error was returned");
}else
{
console.log("Error was not returned")
}
return done();
})
});
});
I received the following output:
․
0 passing (4ms)
1 failing
1) Domain Tests Should return an error when testing:
Error: TestError
at error (/Users/bensudbury/Documents/node_projects/testMochaDomains/test.js:9:11)
at Context.<anonymous> (/Users/bensudbury/Documents/node_projects/testMochaDomains/testMocha.js:6:3)
at Test.Runnable.run (/usr/local/share/npm/lib/node_modules/mocha/lib/runnable.js:194:15)
at Runner.runTest (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:358:10)
at /usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:404:12
at next (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:284:14)
at /usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:293:7
at next (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:237:23)
at Object._onImmediate (/usr/local/share/npm/lib/node_modules/mocha/lib/runner.js:261:5)
at processImmediate [as _immediateCallback] (timers.js:330:15)
As you can see: the domain error handler was short circuited.
This problem seems to be related to the following issues:
https://github.com/visionmedia/mocha/issues/513
While the Node issue has been closed, the issue in mocha is still open.
The workaround that was suggested at: https://gist.github.com/mcollina/4443963 didn't resolve the issue in this case.
I dug through the code of Mocha and found that the problem occurs because mocha wraps the tests in a try catch block. This means that the exception is caught and never sent to the uncaughtException or _fatalException handler depending upon the version of node that you are using.
Your workaround is good, but nodejs domains do definitely catch synchronous errors so I wouldn't change your code but instead change your test. Your new test should look like:
describe("test", function() {
it("should succeed", function(done) {
process.nextTick(function(){
var foo = require("./foo.js");
foo(function() {
console.log("done");
done();
});
})
});
});
I haven't tested this code, but similar code for my example works properly:
it('Should return an error when testing', function(done){
process.nextTick(function(){
error(function(err){
if(err)
{
console.log("Error was returned");
}else
{
console.log("Error was not returned")
}
return done();
});
})
});
I have added a comment to the end of the issue in Mocha to see if it can be resolved:
https://github.com/visionmedia/mocha/issues/513
I'm getting started with mocha testing framework with NodeJS. Success assertions working fine but if the assertion fails, my test timeouts. For asserting I've tried Should and Expect. For example (async code)
it('should create new user', function(done){
userService.create(user).then(function(model){
expect(model.id).to.be(1); //created user ID
done();
}, done)
});
Here the if model id is not 1 then the test timesout instead of reporting failed assertion. I'm sure I'm doing something wrong. Appreciate your help. Thanks!
Shawn's answer works, but there is a simpler way.
If you return the Promise from your test, Mocha will handle everything for you:
it('should create new user', function() {
return userService.create(user).then(function(model){
expect(model.id).to.be(1); //created user ID
});
});
No done callback needed!
expect is throwing an error that is being caught by the promise. Adding a catch condition that calls done fixes this.
it('should create new user', function(done) {
userService.create(user).then(function(model) {
expect(model.id).to.be(1); //created user ID
done();
}).catch(function(e) {
done(e);
})
});
Looks like done is never called. Besides then, you may also need an else to handle the failure.
In my node application I'm using mocha to test my code. While calling many asynchronous functions using mocha, I'm getting timeout error (Error: timeout of 2000ms exceeded.). How can I resolve this?
var module = require('../lib/myModule');
var should = require('chai').should();
describe('Testing Module', function() {
it('Save Data', function(done) {
this.timeout(15000);
var data = {
a: 'aa',
b: 'bb'
};
module.save(data, function(err, res) {
should.not.exist(err);
done();
});
});
it('Get Data By Id', function(done) {
var id = "28ca9";
module.get(id, function(err, res) {
console.log(res);
should.not.exist(err);
done();
});
});
});
You can either set the timeout when running your test:
mocha --timeout 15000
Or you can set the timeout for each suite or each test programmatically:
describe('...', function(){
this.timeout(15000);
it('...', function(done){
this.timeout(15000);
setTimeout(done, 15000);
});
});
For more info see the docs.
I find that the "solution" of just increasing the timeouts obscures what's really going on here, which is either
Your code and/or network calls are way too slow (should be sub 100 ms for a good user experience)
The assertions (tests) are failing and something is swallowing the errors before Mocha is able to act on them.
You usually encounter #2 when Mocha doesn't receive assertion errors from a callback. This is caused by some other code swallowing the exception further up the stack. The right way of dealing with this is to fix the code and not swallow the error.
When external code swallows your errors
In case it's a library function that you are unable to modify, you need to catch the assertion error and pass it onto Mocha yourself. You do this by wrapping your assertion callback in a try/catch block and pass any exceptions to the done handler.
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(function (err, result) {
try { // boilerplate to be able to get the assert failures
assert.ok(true);
assert.equal(result, 'bar');
done();
} catch (error) {
done(error);
}
});
});
This boilerplate can of course be extracted into some utility function to make the test a little more pleasing to the eye:
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(handleError(done, function (err, result) {
assert.equal(result, 'bar');
}));
});
// reusable boilerplate to be able to get the assert failures
function handleError(done, fn) {
try {
fn();
done();
} catch (error) {
done(error);
}
}
Speeding up network tests
Other than that I suggest you pick up the advice on starting to use test stubs for network calls to make tests pass without having to rely on a functioning network. Using Mocha, Chai and Sinon the tests might look something like this
describe('api tests normally involving network calls', function() {
beforeEach: function () {
this.xhr = sinon.useFakeXMLHttpRequest();
var requests = this.requests = [];
this.xhr.onCreate = function (xhr) {
requests.push(xhr);
};
},
afterEach: function () {
this.xhr.restore();
}
it("should fetch comments from server", function () {
var callback = sinon.spy();
myLib.getCommentsFor("/some/article", callback);
assertEquals(1, this.requests.length);
this.requests[0].respond(200, { "Content-Type": "application/json" },
'[{ "id": 12, "comment": "Hey there" }]');
expect(callback.calledWith([{ id: 12, comment: "Hey there" }])).to.be.true;
});
});
See Sinon's nise docs for more info.
If you are using arrow functions:
it('should do something', async () => {
// do your testing
}).timeout(15000)
A little late but someone can use this in future...You can increase your test timeout by updating scripts in your package.json with the following:
"scripts": {
"test": "test --timeout 10000" //Adjust to a value you need
}
Run your tests using the command test
For me the problem was actually the describe function,
which when provided an arrow function, causes mocha to miss the
timeout, and behave not consistently. (Using ES6)
since no promise was rejected I was getting this error all the time for different tests that were failing inside the describe block
so this how it looks when not working properly:
describe('test', () => {
assert(...)
})
and this works using the anonymous function
describe('test', function() {
assert(...)
})
Hope it helps someone, my configuration for the above:
(nodejs: 8.4.0, npm: 5.3.0, mocha: 3.3.0)
My issue was not sending the response back, so it was hanging. If you are using express make sure that res.send(data), res.json(data) or whatever the api method you wanna use is executed for the route you are testing.
Make sure to resolve/reject the promises used in the test cases, be it spies or stubs make sure they resolve/reject.