mongoskin+mocha: How to do clean-up in after() when assertion failed? - node.js

I do the clean-up in an after() call before any other describe. If all tests pass, the clean-up will do the job. But if any test fails, the clean-up code will receive an err: [Error: no open connections].
I think the assertion in the callback of mongodb throws an error cause the connection closed.
That make me confusing:
First, I think the callback of mongodb is the right place to put some assertions;
Second, the assertions will throw error when failed, and cause connection closes;
Finally, the clean-up will failed due to connection closed.
So, what else should I do to make clean-up to do its job even the assertion failed?
I have made a sample code below:
var mongo = require('mongoskin')
, should = require('should')
;
describe('mongo', function() {
var db;
before(function() {
console.log('before');
db = mongo.db('devstack.local:27017/test')
});
after(function(done) {
console.log('after');
db.dropDatabase(function(err) {
should.not.exist(err);// [Error: no open connections]
db.close(done);
});
});
describe('close', function() {
it('should count!=0', function(done) {
db.collection('empty').count(function(err, count) {
count.should.not.equal(0); // use an empty collection to make sure this fail
done();
});
})
})
})

Here's an hypothesis: the connection never happens.
When I run your test suite with:
db = mongo.db('nonexistent:3333/test')
instead of the address you have, I can completely reproduce your error. Note that:
count.should.not.equal(0); fails because count is undefined, not because any of the framework defined by the should module is called.
If I transform the test so that it checks err :
it('should count!=0', function(done) {
db.collection('empty').count(function(err, count) {
should.not.exist(err); // <<< This is where it fails now!
count.should.not.equal(0); // use an empty collection to make sure this fail
done();
});
});
Then the test fails at should.not.exist(err) and err is:
[Error: failed to connect to [nonexistent:3333]]
A couple of thoughts:
Always check err in your callbacks.
In the before callback which establishes the database connection, perform at least one operation which is guaranteed to fail if the connection is not established. You'd want an operation which is as inexpensive to perform as possible. I don't know Mongo very well but this seems to do the trick:
before(function (done) {
db = mongo.db(<put address here>, {safe: true});
db.open(function (err) {
should.not.exist(err);
done();
});
});
This way Mocha will detect the failure right away.

Related

How to unit test a pool connection?

I would like to unit test (mocha & chai) a pool connection (mysql) in my node.js app. I don't know if I'm using the tests in the correct way and, if so, why they are always qualified as "pending".
Giving the following code, how would I test it?
var pool = mysql.createPool({
connectionLimit: 100,
host: 'localhost',
user: 'userName',
password: 'userPass',
database: 'userDatabase',
debug: false
});
I have tried in many ways but it doesn't seem to work. Best I got was this:
describe("Database", function () {
describe("Connection", function () {
it("Should connect without an error", pool.getConnection(function (err, connection) {
expect(err).to.be.null;
})
);
})
});
Which would return, if credentials are correct:
Express server listening on port 8080
Database
Connection
- Should connect without an error
0 passing (15ms)
1 pending
And return, if credentials are incorrect:
Express server listening on port 8080
Database
Connection
- Should connect without an error
1) Uncaught error outside test suite
0 passing (23ms)
1 pending
1 failing
1) Database Connection Uncaught error outside test suite:
Uncaught AssertionError: expected [Error: ER_DBACCESS_DENIED_ERROR: Access denied for user 'userName'#'localhost' to database 'userDatabase'] to be null
Thank you in advance.
What you pass to it in the 2nd argument must be a function. Right now you are doing:
it("Should connect without an error", pool.getConnection(...))
pool.getConnection takes a callback so in all likelihood, it returns undefined. So it looks like this to Mocha:
it("Should connect without an error", undefined)
And this is a pending test because having undefined for the callback is the way to tell Mocha the test is pending. You need to wrap your call to pool.getConnection in a function:
it("Should connect without an error", function (done) {
pool.getConnection(function (err, connection) {
if (err) {
done(err); // Mocha will report the error passed here.
return;
}
// Any possible tests on `connection` go here...
done();
});
});
See testing asynchronous code in the mocha docs. Your it function should look similar to the below.
it('Should connect without an error', function (done) {
pool.getConnection(done);
});
Or, if you want to add assertions inside your callback do the following:
it('Should connect without an error', function (done) {
pool.getConnection((err, connection) => {
try {
expect(connection).to.not.be.null;
expect(connection).to.have.property('foo');
done();
} catch (error) {
done(error);
}
});
});
Note you should preferably test with promises because this allows you to run expect statements on the connection object without extra try/catch/done statements. For example, if pool.getConnection returns a promise, you can do:
it('Should connect without an error', function () {
return pool.getConnection(connection => {
expect(connection).to.have.property('foo');
// more assertions on `connection`
});
});
Also note that these are not "unit tests", but integration tests, as they are testing that two systems work together rather than just your application behaving as expected on its own.

Mocha tests cases not waiting before to complete

I'm writing test cases with mocha in Nodejs and want to reset database data before running the tests. I'm using Knex as query builder for executing queries.
I wrote following logic:
describe('Activities:', function() {
before(funtion(){
activityDBOperations.deleteAll()
.then(function(){
// all records are deleted
});
});
it('it should add a record into Activities table: multiple time activity', function(done) {
activityDBOperations.addRecord(requestParams)
.then(function(data) {
expect(data.length > 0).to.equal(true);
done();
});
});
});
The problem is that test cases start executing and not waiting for deleteAll operation to finish. What I understand is since deleteAll is returning promise, the program execution move forward because of asynchronous nature of promises.
How can I make sure that test cases should run only when deleteAll has finished?
Either provide a callback to your before hook and call it in then:
before(function(done) {
activityDBOperations.deleteAll()
.then(function() {
// all records are deleted
done();
});
});
or, according to Mocha docs, just return a promise from before:
before(function() {
return activityDBOperations.deleteAll();
});
Add return statements so the Promises are actually returned.

Integration testing with mongojs to cover database errors

I'm working with mongojs and writing tests for mocha running coverage with istanbul. My issue is that I would like to include testing db errors.
var mongojs = require('mongojs');
var db = mongojs.connect(/* connection string */);
var collection = db.collection('test');
...
rpc.register('calendar.create', function(/*... */) {
collection.update({...}, {...}, function (err, data) {
if (err) {
// this code should be tested
return;
}
// all is good, this is usually covered
});
});
the test looks like this
it("should gracefully fail", function (done) {
/* trigger db error by some means here */
invoke("calendar.create", function (err, data) {
if (err) {
// check that the error is what we expect
return done();
}
done(new Error('No expected error in db command.'));
});
});
There is a fairly complex setup script that sets up the integration testing environment. The current solution is to disconnect the database using db.close() and run the test resulting in an error as wanted. The problem with this solution arises when all the other tests after that require the database connection fail, as I try to reconnect without success.
Any ideas on how to solve this neatly? Preferably without writing custom errors that might not be raised by next version of mongojs. Or is there a better way of structuring the tests?
What about mocking the library that deals with mongo?
For example, assuming db.update is eventually the function that gets called by collection.update you might want to do something like
describe('error handling', function() {
beforeEach(function() {
sinon.stub(db, 'update').yields('error');
});
afterEach(function() {
// db.update will just error for the scope of this test
db.update.restore();
});
it('is handled correctly', function() {
// 1) call your function
// 2) expect that the error is logged, dealt with or
// whatever is appropriate for your domain here
});
});
I've used Sinon which is
Standalone test spies, stubs and mocks for JavaScript. No dependencies, works with any unit testing framework.
Does this make sense?

Mocha failed assertion causing timeout

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 mocha testing while calling asynchronous function how to avoid the timeout Error: timeout of 2000ms exceeded

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.

Resources