I\'m having issues getting Chai\'s expect.to.throw
to work in a test for my node.js app. The test keeps failing on the thrown error, but If I wrap the test case in try and catch and assert on the caught error, it works.
Does expect.to.throw
not work like I think it should or something?
it(\'should throw an error if you try to get an undefined property\', function (done) {
var params = { a: \'test\', b: \'test\', c: \'test\' };
var model = new TestModel(MOCK_REQUEST, params);
// neither of these work
expect(model.get(\'z\')).to.throw(\'Property does not exist in model schema.\');
expect(model.get(\'z\')).to.throw(new Error(\'Property does not exist in model schema.\'));
// this works
try {
model.get(\'z\');
}
catch(err) {
expect(err).to.eql(new Error(\'Property does not exist in model schema.\'));
}
done();
});
The failure:
19 passing (25ms)
1 failing
1) Model Base should throw an error if you try to get an undefined property:
Error: Property does not exist in model schema.
You have to pass a function to expect
. Like this:
expect(model.get.bind(model, \'z\')).to.throw(\'Property does not exist in model schema.\');
expect(model.get.bind(model, \'z\')).to.throw(new Error(\'Property does not exist in model schema.\'));
The way you are doing it, you are passing to expect
the result of calling model.get(\'z\')
. But to test whether something is thrown, you have to pass a function to expect
, which expect
will call itself. The bind
method used above creates a new function which when called will call model.get
with this
set to the value of model
and the first argument set to \'z\'
.
A good explanation of bind
can be found here.
As this answer says, you can also just wrap your code in an anonymous function like this:
expect(function(){
model.get(\'z\');
}).to.throw(\'Property does not exist in model schema.\');
And if you are already using ES6/ES2015 then you can also use an arrow function. It is basically the same as using a normal anonymous function but shorter.
expect(() => model.get(\'z\')).to.throw(\'Property does not exist in model schema.\');
This question has many, many duplicates, including questions not mentioning the Chai assertion library. Here are the basics collected together:
The assertion must call the function, instead of it evaluating immediately.
assert.throws(x.y.z);
// FAIL. x.y.z throws an exception, which immediately exits the
// enclosing block, so assert.throw() not called.
assert.throws(()=>x.y.z);
// assert.throw() is called with a function, which only throws
// when assert.throw executes the function.
assert.throws(function () { x.y.z });
// if you cannot use ES6 at work
function badReference() { x.y.z }; assert.throws(badReference);
// for the verbose
assert.throws(()=>model.get(z));
// the specific example given.
homegrownAssertThrows(model.get, z);
// a style common in Python, but not in JavaScript
You can check for specific errors using any assertion library:
Node
assert.throws(() => x.y.z);
assert.throws(() => x.y.z, ReferenceError);
assert.throws(() => x.y.z, ReferenceError, /is not defined/);
assert.throws(() => x.y.z, /is not defined/);
assert.doesNotThrow(() => 42);
assert.throws(() => x.y.z, Error);
assert.throws(() => model.get.z, /Property does not exist in model schema./)
Should
should.throws(() => x.y.z);
should.throws(() => x.y.z, ReferenceError);
should.throws(() => x.y.z, ReferenceError, /is not defined/);
should.throws(() => x.y.z, /is not defined/);
should.doesNotThrow(() => 42);
should.throws(() => x.y.z, Error);
should.throws(() => model.get.z, /Property does not exist in model schema./)
Chai Expect
expect(() => x.y.z).to.throw();
expect(() => x.y.z).to.throw(ReferenceError);
expect(() => x.y.z).to.throw(ReferenceError, /is not defined/);
expect(() => x.y.z).to.throw(/is not defined/);
expect(() => 42).not.to.throw();
expect(() => x.y.z).to.throw(Error);
expect(() => model.get.z).to.throw(/Property does not exist in model schema./);
You must handle exceptions that \'escape\' the test
it(\'should handle escaped errors\', function () {
try {
expect(() => x.y.z).not.to.throw(RangeError);
} catch (err) {
expect(err).to.be.a(ReferenceError);
}
});
This can look confusing at first. Like riding a bike, it just \'clicks\' forever once it clicks.
examples from doc... ;)
because you relies on this context:
- which is lost when the function is invoked by .throw
- there’s no way for it to know what this is supposed to be
you have to use one of these options:
One other possible implementation, more cumbersome than the .bind() solution, but one that helps to make the point that expect() requires a function that provides a this
context to the covered function, you can use a call()
, e.g.,
expect(function() {model.get.call(model, \'z\');}).to.throw(\'...\');