Chai, testing that function throws correct error - javascript

Basically, I am practicing testing with Mocha and I wrote a schema where a serial number is supposed to be unique. I want a test that shows that when I try to use that serial number again, it throws the Mongo Error E11000 for duplicate keys.
phaseSchema.statics.createPhase = function(name,sernum,desc){
var phase = mongoose.model('phases', phaseSchema)
var newphase = new phase({NAME: name, SERNUM: sernum,DESC: desc});
newphase.save(function(err,newphase){
if(err)
return console.error(err);
})
}
I've tried a bunch of different ways but I get timeout errors or have the assertion be ignored and I can't figure it out.
I feel like the closest I've gotten is
it("throws error when non unique sequence number is used", function(done){
(function () {
ucdphase.createPhase('d0','develop',0,"desc")
}).should.throw("E11000 duplicate key error index: test.ucdphase.$");
done();
});
but what happens then is it prints the error to the console and then says
"AssertionError: expected [Function] to throw an error.

According chai documentation, you should use expect to start assertion.
Also I don't think you need to use done callback. That callback is used to test asynchronous code.
Try this:
it("throws error when non unique sequence number is used", function(){
expect(function () {
ucdphase.createPhase('d0','develop',0,"desc")
}).to.throw("E11000 duplicate key error index: test.ucdphase.$");
});

Related

Expect expression not to throw any error using Chai

I have a function
export function getFileContent(path: string): any {
const content = readFileSync(path);
return JSON.parse(content.toString());
}
If I want to check if the expression getFileContent(meteFile) throws some definite error I should write something like
expect(getFileContent(meteFile)).to.throw(new SyntaxError('Unexpected token } in JSON at position 82'));
But is there a way to check if the expression doesn't raise any error?
I tried this
expect(getFileContent(metaFile)).not.to.throw();
But got the error
AssertionError: expected { Object (...) } to be a function
So how can I check if the function call doesn't throw any error?
You can check if the function call doesn't throw an error using
assert.doesNotThrow method
Here is an example from the documentation
assert.doesNotThrow(fn, 'Any Error thrown must not have this message');
assert.doesNotThrow(fn, /Any Error thrown must not match this/);
assert.doesNotThrow(fn, Error);
assert.doesNotThrow(fn, errorInstance);
For further understanding checkout it's documentation: assert.doesNotThrow
It seems like you are using expect, so then you can use expect(fn).to.not.throw to make sure it doesn't throw an error.
expect(myFn).to.not.throw(); // makes sure no errors are thrown
expect(myFn).to.not.throw(SomeCustomError, "the message I expect in that error"); // not recommended by their docs
You should probably look at the docs for this.

What does it mean an object starts with an array in javascript

Sometimes I got an error like this which fired from catch(err => console.log(err)):
{ [Error: SQLITE_ERROR: no such table: table_name] errno: 1, code: 'SQLITE_ERROR' }
It looks like an object and yeah it is, but why is there an array in the beginning of it?
How can I get that array?
I want to get that error message:
Error: SQLITE_ERROR: no such table: table_name
To decide what to do next.
I thought the error message was part of the key name so I tried:
Object.keys(err);
But return an array like this:
["errno", "code"]
How to get that error message?
IIRC.... sqlite uses the built in Error object. What you see in brackets is not an array; it is actually a human-readable error description string which is in brackets (perhaps for emphasis).
try {
....
} catch (err) {
console.log(err.message); // this should be your error message.
}
Whatever is throwing the error is not throwing a simple string. Try a different logging method to get a better idea of what is being returned, as console.log may not be giving you the correct value of the error when being logged.
Try using console.log(JSON.parse(JSON.stringify(error))) and see what is returned. It may be that you need to do something like console.log(error.response.message) or something similar to get a more useful string you can use.
console.log may also be running a custom toString method on the returned object, like this, for example: https://repl.it/#Twinbird24/WingedCloudyTrials

Mocha test timeouts when testing length of array

I'm testing a little node module with mocha and expect.js and I'm having a bit of a headache.
This is the test definition:
it('Should return an expansion array that contains all the objects that comes along within a single group', function (done) {
visibility.expand(data, config.data, companies, groups, function (error, expansion) {
expect(error).to.be(null);
expect(expansion).to.be.an('array');
expect(expansion).to.have.length(2);
expect(expansion.toString()).to.eql([testCompaniesIds[2], testCompaniesIds[3]].toString());
done();
});
});
data is a wrapper for database access and functionality. I initialize it in the before method of my test suite.
config.data holds an object with config params (not relevant)
companies is an array of strings
groups is an array of strings
The problem that i'm facing is that when Mocha reaches this line
expect(expansion).to.have.length(2);
and the length is different from 2, instead of throwing an error like: Expected 2 but length was 1 it simply stops and throw an error because of the timeout.
I verified that the test executed until that line.
A bit more of information: the method that I'm testing receives an array of company names and an array of group names. Each group contains an array of company database id's.
The expected behaviour of the method is to return an array with corresponding company id's merged with the array of company id's belonging to the group object.
Edit 2 due to possible duplicate: I was wrong. It was indeed executing in the scope of a promise.
Edit due to possible duplicate: the expectation in not executing in the scope of a promise (when using promises and executing expect in either resolve or reject functions is the promise who catch the error).
Thanks in advance!
Wrapping all the test between a try-catch like this:
it('Should return an expansion array that contains all the objects that comes along within a single group', function (done) {
visibility.expand(data, config.data, [testCompanies[0].name, testCompanies[1].name], [testGroups[0].name, testGroups[1].name], function (error, expansion) {
try {
expect(error).to.be(null);
expect(expansion).to.be.an('array');
expect(expansion).to.have.length(2);
expect(checkIdIsContainedInArray(testCompaniesIds[0], expansion)).to.be(true);
expect(checkIdIsContainedInArray(testCompaniesIds[1], expansion)).to.be(true);
expect(checkIdIsContainedInArray(testCompaniesIds[2], expansion)).to.be(true);
expect(checkIdIsContainedInArray(testCompaniesIds[3], expansion)).to.be(true);
done();
} catch (e) {
done(e);
}
});
});
This test throws an error due to array length (is 4 and should be 2):
Error: expected [ '464142414441464142414441',
'464142414441464142414442',
'464142414441464142414443',
'464142414441464142414444' ] to have a length of 2 but got 4
Instead of:
Error: timeout of 2000ms exceeded
which can mean anything.
Debugging expect.js I saw that it was throwing an error but Mocha didn't manage to capture it.
Although this solution might not be as elegant as desired, at least it gives the desired feedback instead of a timeout.
You are executing the expectations in a callback function.
This means that the code is being executed in the visibility.expand method.
I'm pretty sure that the behaviour that you are seeing is because of the use of promises... are you using promises in the visibility.expand method?
I believe this is actually working as expected.
The Mocha tests are only able to end the async call when the done() is actually called. Since an error is thrown outside of the scope of the mocha execution context, it never reaches that block of code and the execution basically runs out of time.
I can't seem to find any official documentation stating this, but here's a few (somewhat related hopefully?) references -
https://github.com/pouchdb/pouchdb/issues/1339#issuecomment-34739526
http://chaijs.com/guide/styles/#expect //search around for error handling on callbacks
Is there a way to get Chai working with asynchronous Mocha tests?
Mocha failed assertion causing timeout
Edit - Actually, I'm seeing that I had it backwards, it should be catching the exception for sure. I would relook at the original code again and see if you have everything correct. Maybe update expect.js and see if it has some issues on your current version (or file a ticket). I use chai with the expect library and am able to get it to error fine. http://chaijs.com/
Code example with error being thrown correctly -
var chai = require('chai')
, expect = chai.expect;
describe('something', function () {
var j = [1];
it('does something.. ', function (done) {
setInterval(function () {
console.log('its done');
expect(j).to.be.length(2);
done();
}, 1000);
});
});
//Uncaught AssertionError: expected [ 1 ] to have a length of 2 but got 1

mocha with nodejs assert hangs/timeouts for assert(false) instead of error

I have this kind of a mocha test:
describe 'sabah', →
beforeEach →
#sabahStrategy = _.filter(#strats, { name: 'sabah2' })[0]
.strat
it 'article list should be populated', (done) →
#timeout 10000
strat = new #sabahStrategy()
articles = strat.getArticleStream('barlas')
articles.take(2).toArray( (result)→
_.each(result, (articleList) →
// I make the assertions here
// assert(false)
assert(articleList.length > 1)
)
done()
)
The problem is, whenever I do assert(false), the test hangs until the timeout, instead of giving an assertion error, why?
Edit:
For example if I have these two tests
it 'assert false', (done) →
assert(false)
done()
it 'article link stream should be populated', (done) →
#timeout 20000
articles = #sabahStrategy.articleLinkStream('barlas')
articles.pull((err, result)→
console.log('here')
assert(false)
console.log('after')
assert(!err)
assert(result.length > 1);
_.each(result, (articleList) →
assert(articleList.link)
)
done()
)
The first one, gives the assertion error as expected, the second one, logs here, and hangs at assert(false) so after is never logged. It has something to do with articles being a stream and the assertion is within a pull callback, this is from the highland.js API.
Solved Edit:
So according to Paul I fixed the problem with this code:
it 'article stream should be populated', (done) →
#timeout 30000
articles = #sabahStrategy.articleStream('barlas')
articles.pull((err, result) →
try
# assert false properly throws now.
assert(false)
assert(!err)
assert(result.length == 1)
assert(result[0].body)
assert(result[0].title || result[0].title2)
done()
catch e
done(e)
)
Edit2:
I've produced a simplified version of the problem:
h = require('highland')
Q = require('q')
describe 'testasynchigh', →
beforeEach →
#deferred = Q.defer()
setTimeout((→
#deferred.resolve(1)
).bind(this), 50)
it 'should throw', (done) →
s = h(#deferred.promise);
s.pull((err, result) →
console.log result
assert false
done()
)
I see your version does indeed work #Louis, but if you involve promises into the mix, mocha can't handle the problem, so it will hang in this example. Also try commenting out the assert false and see it passes.
So Louis I hope I got your attention, could you explain the problem, and try catch looks ugly indeed and I hope you find a reasonable solution to this.
Because that's what you're telling it you want to do, when you add the 'done' callback.
The way to actually do this test is to call return done(err) if the assertion would fail, where err is any string or error object you want reported.
First, when your assertion fails, the program throws an exception and never reaches done(), which is why you're not seeing done get called. This is how assertions are supposed to work, however since you're in an async test, the result is that the callback never fires, which is why you hit the timeout.
Second, as my original answer said err is any error you want to send out from the test. It can be a string error message or a full-on Error object subclass. You create it and then pass it to done() to indicate the test failed.
The better way to structure your code in an asynchronous test is to use your tests as simple booleans, rather than as assertions. If you really want to use assert, then wrap it in a try..catch. Here's a couple examples:
if(err) return done(err); // in this case, err is defined as part of the parent callback signature that you have in your code already.
if(result.length < 1) return done('Result was empty!');
Finally, if you really want to assert, then you can:
try{
assert(!err);
}catch(e){
return done(e);
}
I'm calling return done(err) rather than done(err) because it stops the rest of the code from executing, which is normally what you want.
For anyone with same issue: You should make sure that done() gets called even after an assert fails, like in the following code:
try {
// your asserts go here
done();
} catch (e) {
done(e);
}
When I use Highland.js with a super simple test, Mocha catches the failing assert without any problem:
var _ = require("highland");
var fs = require("fs");
var assert = require("assert");
describe("test", function () {
it("test", function (done) {
var s = _([1, 2, 3, 4]);
s.pull(function (err, result) {
console.log(result);
assert(false);
done();
});
});
});
This suggests that the problem in your example is not Mocha, nor Highland.js. If the articleLinkStream object (or articleSream; it seems to change from snippet to snippet) is custom code then maybe that code is buggy and actually swallows exceptions rather than let them move up the stack.

Catching out-of-scope errors with Mocha and Chai

I am modifying a node.js library to support true asynchronous operations.
I am having troubles with Mocha and Chai to make this (a similar) test pass.
it('should throw an error', function() {
expect(function() {
process.nextTick(function() {
throw new Error('This is my error');
});
}).to.throw(Error);
});
The problem is - because of the nextTick - that the Error is thrown out of scope of it and besides the test failing, Mocha also outputs the below.
Uncaught Error: This is my error
What is the proper way to structure this test in order to make it succeed?
Hmm... in a full-fledged application what I'd do is probably use something like Sinon to check that the method that should throw an error has been called and is throwing.
In code where you cannot do this, then the following method would trap the exception:
var expect = require("chai").expect;
var domain = require("domain");
it('should throw an error', function(done) {
var d = domain.create();
d.on('error', function (err) {
// Exit the current domain.
d.exit();
// We must execute this code at the next tick.
process.nextTick(function () {
console.log(err); // Just to show something on the console.
expect(err instanceof Error).to.be.true;
done();
});
});
d.run(function () {
process.nextTick(function() {
throw new Error('This is my error');
});
});
});
This code creates a "domain" stored in d. A domain will emit error events on uncaught exceptions that happen in it so we run the test inside the domain (d.run(...)) we've created and wait for an exception to happen (d.on('error', ...). We check that it is an Error object. (In a real test, I'd also check the error message.) When we are finished we call done() to tell Mocha that the asynchronous test is over.
The handler for error events calls d.exit(). This is to make it so that Mocha can catch the error normally if the assertion (expect(err instanceof Error)...) turns out to fail. If we do not exit the domain, then the domain will trap the error. Also, the check itself must be performed on the next tick to be outside the d domain.
Is Using domain A Problem?
NO!
The documentation for domain comes with some warnings about shutting down operations once an uncaught exception is caught when running an ongoing process, like a server. Then the thing to do is to clean what can be cleaned and exit as soon as possible. However, using domain in a test does not differ from what Mocha is already doing. The way Mocha catches unhandled exceptions in asynchronous code is by using process.on('uncaughtException'. Upon catching an unhandled exception Mocha marks the current test as failed and continues. Yet the documentation regarding uncaughtException says "Don't use it, use domains instead. If you do use it, restart your application after every unhandled exception!"
Ergo, anyone who has a problem with using domain should not be using Mocha in the first place.
You're trying to catch an exception on the incorrect function, the container for the function that throws. Also, because the function is wrapped in nextTick it is executed in a different stack and therefore the exception cannot be captured (this is simply a JS thing unfortunately).
Try this instead:
it ('should throw an error', function (done) {
process.nextTick(function () {
var myFn = function () { throw new Error() };
expect(myFn).to.throw(Error);
// Tell mocha the test is complete
done();
});
});
Update: There is no proper way to structure this test to make it pass as you cannot catch the exception in this scenario. Perhaps update your code to use callbacks to handle errors:
function doSomethingUnsafe() {
try {
// Run code here that may cause exceptions...
callback(null, 'Woohoo! No errors!');
} catch (e) {
callback (e, null);
}
}

Categories