I run many Mocha tests that look like this :
it('should fail blabla', function () {
return <functionThatReturnsAPromise>()
.then(function () {
assert(false);
}, function (err) {
assert.strictEqual(err.message, 'Foobar is undefined');
assert.strictEqual(err.code, 'EUNDEFINEDFOOBAR');
});
});
The only things that vary among my tests are the function, the code and the message.
I wanted to factorize the code like this:
function shouldBeRejected(promise, code, message) {
return promise
.then(function () {
assert(false);
}, function (err) {
assert.strictEqual(err.code, code);
assert.strictEqual(err.message, message);
});
}
(...)
it('should fail blabla', function () {
return shouldBeRejected(<functionThatReturnsAPromise>(),
'EUNDEFINEDFOOBAR', 'Foobar is undefined');
});
It works but when some tests failed, all my backtraces reference the shouldBeRejected function and so, I'm losing all information about the real test that failed.
Is there a way to get a more "intelligent" backtrace pointing to the shouldBeRejected calls that indirectly raise the error? Maybe with a captureStackTrace somewhere?
Related
I'm working on adding test coverage to a Node project I'm working on using Jest. The code I'm testing is throwing errors within promises resulting in an UnhandledPromiseRejectionWarning message being logged to the console.
While writing tests, I can pretty easily identify these issues and resolve them, but these warnings aren't actually causing Jest to mark the tests as failed, so our CI won't catch it. I've searched around for any suggestions and haven't found much.
I did find in Node's documentation that you can catch these warnings and handle them...
process.on('unhandledRejection', (error) => {
throw error; // Or whatever you like...
});
So it seems like it would be pretty straightforward to add this code into my test cases. After all, an Error thrown within the test should cause the test to fail...
describe('...', () => {
it('...', () => {
process.on('uncaughtRejection', (error) => {
throw error;
});
// the rest of my test goes here
});
});
Unfortunately the behavior I'm seeing is that the error does get thrown, but Jest doesn't catch it and fail the test. Instead, Jest crashes with this error and the tests don't continue to run. This isn't really desirable, and seems like incorrect behavior.
Throwing an error outside of the uncaughtRejection handler works as expected: Jest logs the thrown error and fails the test, but doesn't crash. (i.e. the test watcher keeps watching and running tests)
The way I've approached this is very much tied into the way I write my functions - basically, any function that uses promises should return a promise. This allows whatever code calls that function to handle catching errors in any way it sees fit. Note that this is my approach and I'm not going to claim this is the only way to do things.
For example... Imagine I'm testing this function:
const myFunction = () => {
return doSomethingWithAPromise()
.then(() => {
console.log('no problems!');
return true;
});
};
The test will look something like this:
describe('...', () => {
it('...', () => {
return myFunction()
.then((value) => {
expect(value).toBe(true);
});
});
});
Which works great. Now what happens if the promise is rejected? In my test, the rejected promise is passed back to Jest (because I'm returning the result of my function call) and Jest can report on it.
If, instead, your function does not return a promise, you might have to do something like this:
const myOtherFunction = () => {
doSomethingWithAPromise()
.then(() => {
console.log('no problems!');
return true;
})
.catch((err) => {
// throw the caught error here
throw err;
});
};
Unlike the example above, there is no (direct) way for Jest to handle a rejected promise because you're not passing the promise back to Jest. One way to avoid this might be to ensure there is a catch in the function to catch & throw the error, but I haven't tried it and I'm not sure if it would be any more reliable.
Include the following content in Jest's setupFiles:
if (!process.env.LISTENING_TO_UNHANDLED_REJECTION) {
process.on('unhandledRejection', reason => {
throw reason
})
// Avoid memory leak by adding too many listeners
process.env.LISTENING_TO_UNHANDLED_REJECTION = true
}
Courtesy of stipsan in https://github.com/facebook/jest/issues/3251#issuecomment-299183885.
module:
export function myPromise() {
return new Promise((resolve, reject) => {
const error = new Error('error test');
reject(error);
});
}
test:
import { myPromise } from './module';
it('should reject the promise', () => {
expect.assertions(1);
const expectedError = new Error('error test');
myPromise().catch((error) => {
expect(error).toBe(expectedError);
});
From the node documentation site we can see that The process object is an instance of EventEmitter.
Using the emit function from process we can trigger the errors like uncaughtRejection and uncaughtException programmatically when needed.
it("should log the error", () => {
process.emit("unhandledRejection");
...
const loggerInfo = jest.spyOn(logger, "info");
expect(loggerInfo).toHaveBeenCalled();
});
Not sure if this helps, but you can also assert for promise rejections as such
index.js
module.exports = () => {
return Promise.reject('it didnt work');
}
index.spec.js
const thing = require('../src/index');
describe('rejected promise', () => {
it('should reject with a reason', ()=> {
return expect(thing()).rejects.toEqual('it didnt work');
});
});
How to get asserts in mocha before blocks to work? If I am not suppose to do this let me know. Currently, using promises, if I get an error using an catch block, I'll add an assert to fail the before block. WHat I want is it to fail the describe block, but instead I get two possible outcomes. 1. My test suite completely crashes, 2. I have to wait for each timeout to hit for each test because the before block failed.
before(function (done) {
promise()
.then(function () {
done();
})
.catch(function (error) {
assert(!error);
done();
});
});
I even tried this, thinking, maybe the done was never called.
before(function (done) {
promise()
.then(function () {
//no done here
})
.catch(function (error) {
assert(!error);
});
.finally(function () {
done();
});
});
So far to avoid crashing and waiting, and to make it work, I have done this:
var beforeError;
before(function (done) {
promise()
.then(function () {
done();
})
.catch(function (error) {
beforeError = error;
done();
});
});
it('test something', function () {
assert(beforeError, 'Before block failed with error.');
});
I am really curious if there is a better way to go about this so that if my before/beforeEach/after/afterEach blocks fail, it doesn't cause me to wait ages or my suite to crash! Thanks S/O community! :)
I can't speak to your use of the done callback, but mocha 3.0 supports promises in before hooks now. Were I to write this, I would let the returned promise throw its own error, which will fail the before hook without breaking the suite.
before(function () {
return promise(<async behavior here>);
});
I am trying to write a test case for one of my REST Apis using mocha.
My Rest api looks like this:
server.route({
method : "DELETE",
path : "/local/{id}",
handler: function (request, reply) {
var id = request.params.id;
return getId(id)
.then(function (result) {
return testFunction(result, id, reply);
})
.catch (function (err) {
reply(400).err;
})
}
function testFunction(result, id, reply) {
return update(id, result)
.then(function (resp) {
reply(resp);
stopSomething(id)
.then(function () {
//do some work
return Bluebird.resolve(data);
})
.catch(function (error) {
//handle error & just log..user does not need to know
//stopSomething is a promise chain which runs in background
});
})
.catch(function (err) {
//handle error and reply to user
});
}
});
To test this I wrote the following test case:
describe("with an valid id", function () {
var stopSomethingStub;
var result
before(function () {
stopSomethingStub = Sinon.stub(stopSomethinbObject, "stopSomething", function () {
return Bluebird.resolve();
});
return new Request("DELETE", "/local/id123").inject(server)
.then(function (data) {
result = data;
});
});
after(function () {
//do clean up of stubs
});
it("deletes id", function () {
expect(result.statusCode).to.equal(200);
expect(stopSomethingStub.called).to.be.true;
//Few more checks
});
}
Right now, the "it" block executes immediately after receiving the 200 Ok response for the DELETE Request. However I would like it to finish the entire promise chain before checking the assertion. stopSOmethingStub.called is shown as false if I keep it as the first expect block. However if I keep it as the last assertion it works. I think this is some timing issue.
I'm using this polyfill for ES6 promises and Mocha / Chai.
My assertions for the promises are not working. The following is a sample test:
it('should fail', function(done) {
new Promise(function(resolve, reject) {
resolve(false);
}).then(function(result) {
assert.equal(result, true);
done();
}).catch(function(err) {
console.log(err);
});
});
When I run this test it fails due to timeout. The assertion failure that was thrown in the then block is caught in the catch block. How can I avoid this and just throw it straight to Mocha?
I could just throw it from the catch function, but then how would I make assertions for the catch block?
If your Promise has a failure, it will only call your catch callback. As a result, Mocha's done callback is never called, and Mocha never figures out that the Promise failed (so it waits and eventually times out).
You should replace console.log(err); with done(err);. Mocha should automatically display the error message when you pass an error to the done callback.
I ended up solving my problem by using Chai as Promised.
It allows you to make assertions about the resolution and rejections of promises:
return promise.should.become(value)
return promise.should.be.rejected
A pattern I use in my Mocha/Chai/es6-promise tests is the following:
it('should do something', function () {
aPromiseReturningMethod(arg1, arg2)
.then(function (response) {
expect(response.someField).to.equal("Some value")
})
.then(function () {
return anotherPromiseReturningMethod(arg1, arg2)
})
.then(function (response) {
expect(response.something).to.equal("something")
})
.then(done).catch(done)
})
The last line is odd looking, but calls Mocha's done on success or on error.
One problem is if the last then returns something, I then need to noop()* before both the then and the catch:
it('should do something', function () {
aPromiseReturningMethod(arg1, arg2)
.then(function (response) {
expect(response.someField).to.equal("Some value")
})
.then(function () {
return anotherPromiseReturningMethod(arg1, arg2)
})
.then(_.noop).then(done).catch(done)
})
*Lodash's noop().
Would love to hear any critiques of this pattern.
I have written own thin mongodb wrapper for Node.js to eliminate code repetition.
However, I am having problems with asynchronous unit tests run with Mocha and Should.
What happens is that any thrown exception by Should library is being caught by MongoDB driver instead of Mocha.
I.e., neither Mocha catches the error, nor done() function gets called. As a consequence, Mocha prints out an error Error: timeout of 2000ms exceeded.
Snippet of wrapper module db.js
var mongodb = require('mongodb').MongoClient;
exports.getCollection = function(name, callback) {
mongodb.connect(dbConfig.dbURI, {auto_reconnect: true}, function(err, db) {
if (err)
return callback(err, null);
db.collection(name, {strict: true}, callback);
});
};
Mocha test.js
var should = require('should');
var db = require('./db.js');
describe('Collections', function() {
it.only('should retrieve user collection', function(done) {
db.getCollection('user', function(err, coll) {
should.not.exist(err);
coll.should.be.a('object');
// HERE goes an assertion ERROR
coll.collectionName.should.equal('user123');
done();
});
});
});
The same behaviour can be simulated by this simple test.js
var should = require('should');
var obj = {
call: function(callback) {
try {
console.log('Running callback(null);');
return callback(null);
}
catch(e) {
console.log('Catched an error:', e);
}
}
};
describe('Test', function() {
it('should catch an error', function(done) {
obj.call(function(err) {
should.exist(err);
done();
});
});
});
Is there any way to workaround the issue? There must be a way to test such code.
Just by an accidental luck I spotted a GitHub fork dealing with a different problem, but the code lead me to realise I can use a simple trick to make Mocha to catch the assert exceptions:
describe('Test', function() {
it('should catch an error', function(done) {
obj.call(function(err) {
try {
should.exist(err);
done();
} catch (err) {
done(err);
}
});
});
});
I.e. wrapping the should calls into try/catch block and calling done(err) in the catch section does exactly what is expected :
Test passes successfully if no assertion error occurs
Test fails in case of assertion error thanks to done() function accepting an error argument