Without the try catch blocks, the test fails normally when there is an assertion error
describe("POST /foo", function () {
it('should create a foo', async function () {
const response = await request.post("/foo").send(data);
expect(response.status).to.eql(200); //Assertion error
}).timeout(10000);
})
//+ expected - actual -500 + 200 1 failing
However, the test passes if I wrap it with try/catch block
describe("POST /foo", function () {
it('should create a foo', async function () {
try {
const response = await request.post("/foo").send(data);
expect(response.status).to.eql(200); //Assertion error
} catch (err) {
}
})
})
//1 passing
Assertion libraries (like chai) have functions like expect and assert that will throw exceptions when the condition they are checking for fails. A test runner (mocha in this case) can use this fact to detect test failures by executing the test case within a try/catch block. If an exception is thrown by the test case (i.e., by the assertion library), the test is deemed to have failed.
If you look at mocha's documentation, for example, you will see that:
Mocha allows you to use any assertion library you wish...generally,
if it throws an Error, it will work!
So in pseudocode, mocha is doing something like:
function testRunner() {
try {
testCase('should create a foo'); // run a test case defined by the user
} catch (e) {
console.log('The test has failed');
return;
}
console.log('The test has succeeded.');
}
If you wrap all of the code in your test case in a try/catch block, as in your second example, you are preventing mocha from seeing exceptions raised by the assertion library's expect statement. (The catch block you define is "swallowing" the exception). mocha, seeing no exception, assumes there was no problem, and the test is marked as passed.
An eslint promise/no-nesting rule violation will occur for the following example:
return foo()
.catch(handleErrorAndReject('foo'))
.then(() => bar().catch(handleErrorAndReject('bar')))
.then(() => baz().catch(handleErrorAndReject('baz')))
Note that handleErrorAndReject is a curried function like this:
const handleErrorAndReject = action => error => {
console.warn(`${action} fails with error`, error)
res.status(400).send(error.message).end()
return Promise.reject(error)
}
But the nested catches seem necessary in this case, because they vary by stage. Is this a case where I should just eslint-disable, or is there a better way to structure this code?
I have a code like this (simplified):
getStreamFor(path) {
// both, remote and local, return a Promise
if(...) { return getRemoteFileStream(path); }
else { return getLocalFileStream(path); }
}
getRemoteFileStream(path) {
// should throw in my case (MyError)
const validPath = validatePath(path);
return readStreamIfValid(validPath);
}
and in the test case:
it('should throw MyError', () => {
return getStreamFor(path)
.then(() => {})
.catch(error => expect(error).to.be.instanceOf(MyError));
});
The problem is, that when the validatePath(path) Method throws (due to invalid path), nothing get caught in the test case promise. The output in my terminal / console is a regular exception as if it was uncaught.
Does anybody have an idea, why the the Promise wouldn't recognize the throw? How can I fix it without probably surrounding the call in the test case with another "try catch" (since the promise should do that for me)?
Maybe there is a general best practise how to structure Promises in order to avoid error swallowings like these?
Thanks for your help!
The problem here is that validatePath() is not part of the promise chain returned by getRemoteFileStream()
One possible solution is the following:
getRemoteFileStream(path) {
return Promise.resolve()
.then(() => validatePath(path))
.then(validPath => readStreamIfValid(validPath));
}
An exception thrown by validatePath() would now be handled in the Promise's catch handler.
I run fairly complex piece of code in Node 6.x, and I'm unable to replicate the problem in simple conditions. The gist of the relevant part of the app is
// app.js
someCoWrapper(function*() {
yield hapiServerStart();
// unreachable
...
})
.catch(console.error);
// some-co-wrapper.js
function someCoWrapper(genFn) {
return co(genFn).then(
(result) => new Promise((resolve) => resolve(result)),
(error) => {
return new Promise((resolve, reject) => {
return reject(error)
})
}
)
}
// some-callback-called-inside-serverStart.js
...
assert(..., 'I was thrown from server init callback');
...
The wrapper for co was there to switch between promise implementations, and it was meant to be transparent (it failed).
Here I expect co promise to catch an exception from generator and log it to console.error. This works as expected with node debug app.js. In Jetbrains (PhpStorm 2016), it works with Run. But it throws an uncaught exception with Debug, call stack is really obscure:
The problem doesn't occur when yield hapiServerStart(); is secured with try...catch (naturally).
Changing Promise implementation helps, both
global.Promise = require('es6-promise').Promise;
and
global.Promise = require('bluebird');
work. And the most interesting thing is that changing the code above to
return co(genFn)
.then(
(result) => Promise.resolve(result)),
(error) => Promise.reject(error))
);
helps, too!
Too many moving parts are there, and the fact that it occurs in Jetbrains product suggests that there may be something fishy with it.
I would be grateful for any comments that may shed some light on what happens here.
And the real quesion is how new Promise((err) => reject(err)) differs from Promise.reject(err) in Node/V8 implementation? Is there something that may cause it to misbehave on some conditions (not just in debugger but in production)?
Using Mocha, I am attempting to test whether a constructor throws an error. I haven't been able to do this using the expect syntax, so I'd like to do the following:
it('should throw exception when instantiated', function() {
try {
new ErrorThrowingObject();
// Force the test to fail since error wasn't thrown
}
catch (error) {
// Constructor threw Error, so test succeeded.
}
}
Is this possible?
should.js
Using the should.js library with should.fail
var should = require('should')
it('should fail', function(done) {
try {
new ErrorThrowingObject();
// Force the test to fail since error wasn't thrown
should.fail('no error was thrown when it should have been')
}
catch (error) {
// Constructor threw Error, so test succeeded.
done();
}
});
Alternative you can use the should throwError
(function(){
throw new Error('failed to baz');
}).should.throwError(/^fail.*/)
Chai
And with chai using the throw api
var expect = require('chai').expect
it('should fail', function(done) {
function throwsWithNoArgs() {
var args {} // optional arguments here
new ErrorThrowingObject(args)
}
expect(throwsWithNoArgs).to.throw
done()
});
You can try using Chai's throw construct. For example:
expect(Constructor).to.throw(Error);
Chai now has
should.fail() and expect.fail()
https://github.com/chaijs/chai/releases/tag/2.1.0
2017 answer if you need to do this with async code: using await and not needing any other libraries.
it('Returns a correct error response when making a broken order', async function(){
this.timeout(5 * 1000);
var badOrder = {}
try {
var result = await foo.newOrder(badOrder)
// The line will only be hit if no error is thrown above!
throw new Error(`Expected an error and didn't get one!`)
} catch(err) {
var expected = `Missing required field`
assert.equal(err.message, expected)
}
});
Note the poster was only doing sync code, but I expect a lot of people using async were led here by the question title!
Mocha in default is using Assert from node.js (https://nodejs.org/api/assert.html). You don't need any external libraries to check if a method throws an error.
Assert has a method - assert.throws, it has three parameters, but only two really matters here:
function - here pass function, not function call
error - here pass or object constructor or function for checking the error
Let's imagine that you have a function called sendMessage(message) which throws an error when message parameter is not set. Function code:
function sendMessage(message) {
if (!message || typeof message !== 'string') {
throw new Error('Wrong message');
}
// rest of function
}
Ok, so in order to test it, you need additional function to cover input. Why? Because assert.throws doesn't give any opportunity to pass parameters to the function which going to be tested.
So instead of
// WRONG
assert.throws(sendMessage, Error); // THIS IS WRONG! NO POSSIBILITY TO PASS ANYTHING
you need to create anonymous function:
// CORRECT
assert.throws(() => {
sendMessage(12); // usage of wanted function with test parameters
}, Error)
Can you see the difference? Instead of passing function directly, I have put the function call inside anonymous function, in purpose of calling it with a prepared input.
What about the second parameter. It depends from what kind of error should be thrown, in above example Error object was thrown, so I had to put there Error. In result of this action, assert.throws compares if thrown object is object of the same type. If instead of Error something different will be thrown, then this part needs to be changed. For example instead of Error I will throw a value of type String .
function sendMessage(message) {
if (!message || typeof message !== 'string') {
throw 'Wrong message'; // change to String
}
// rest of function
}
Now the test call
assert.throws(() => {
sendMessage(12); // usage of wanted function with test parameters
}, (err) => err === 'Wrong message')
Instead of Error in second parameter I have used the comparison function in order to compare thrown error with the expectation.
MarkJ's accepted answer is the way to go and way simpler than others here.
Let me show example in real world:
function fn(arg) {
if (typeof arg !== 'string')
throw TypeError('Must be an string')
return { arg: arg }
}
describe('#fn', function () {
it('empty arg throw error', function () {
expect(function () {
new fn()
}).to.throw(TypeError)
})
it('non-string arg throw error', function () {
expect(function () {
new fn(2)
}).to.throw(TypeError)
})
it('string arg return instance { arg: <arg> }', function () {
expect(new fn('str').arg).to.be.equal('str')
})
})
If you don't want to wrap a whole lot of source into the expect parameter, or if you have many arguments to pass and it just gets ugly, you can still do this with the original syntax just fine by leveraging the done argument that is provided (but was originally ignored):
it('should throw exception when instantiated', function(done: Done) {
try {
new ErrorThrowingObject();
done(new Error(`Force the test to fail since error wasn't thrown`));
}
catch (error) {
// Constructor threw Error, so test succeeded.
done();
}
}
Because you're using done here, it allows you go execute arbitrary code above it in the try, then specify exactly where in your source you'd like to record the failure.
Normally, someone may be tempted to throw or assert(false), but these will both be caught by the catch of the try, and cause you to do some meta-checking to determine if the error you caught was the expected error from your test, or if it was the final determination that your test failed. That's just a mess.
If you are using should.js you can do (new ErrorThrowingObject).should.throw('Option Error Text or Regular Expression here')
If you don't want to should a separate library, you could also do something like this:
it('should do whatever', function(done) {
try {
...
} catch(error) {
done();
}
}
This way, you know the error is caught if the test finishes. Otherwise, you will get a timeout error.
With Chai throw (ES2016)
http://chaijs.com/api/bdd/#method_throw
For clarity...
This works
it('Should fail if ...', done => {
let ret = () => {
MyModule.myFunction(myArg);
};
expect(ret).to.throw();
done();
});
This doesn't work
it('Should fail if ...', done => {
let ret = MyModule.myFunction(myArg);
expect(ret).to.throw();
done();
});