Integration testing with mongojs to cover database errors - node.js

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?

Related

testing external api calls in node.js using mocha.js

I'm trying to write tests for my npm module, which takes care of communicating with my backend api. this module will sit inside a cordova android app, and will take care of any api calls. the issue that i'm having seems to be an understanding of mocha, but i've had a good look around the internet and can't find a solution so i turn to the masses. As an example, i have a function like
test: function() {
request.get({
url: defaultHost,
headers: {
}
}, function(err, httpResponse, body) {
if(err) {
return err;
} else {
console.log(body);
return body;
}
});
}
this works will. i'm now trying to create the test for it in mocha. the problem that i'm getting is that i have no idea how to get the return function from the .get call into the mocha test. the api returns json, so i know that i'm going to have to be doing an is equal comparison, but at the moment i can't even get it to print the results. i think the problem with is that with my other mocha tests that i can get working, they all have an argument that you pass in where as this doesn't. my current code for the test looks like this.
describe('#test', function() {
it('tests api reachability;', function() {
var test = new test();
});
});
if someone can explain what is required afterwards or even just point me in the right direction on google, that would be awesome. i'm normally pretty good at the google, but failing to find direction on this one.
I think nock will solve this issue. Let's assume you sending get request to some resource (http://domain.com/resource.json) and the tests would be like this:
var nock = require('nock');
// ...
describe('#test', function() {
beforeEach(function () {
nock('http://domain.com')
.get('resource.json')
.reply(200, {
message: 'some message'
});
});
it('tests api reachability;', function() {
var test = new test();
});
});

Sails.js and Mocha: Using supertest to create a new model

I'm currently setting up testing infrastructure for my Sails app, and it was going smoothly until I tried testing API requests with supertest.
I'm trying to test some of my controller methods (that I implemented instead of using the default blueprint routes), but it seems like the API request isn't even going through. The reason I think this is because I can run npm test and this code will run fine, but if I change the POST path to /datamodel/create5, where create5() does NOT exist as a controller method, it still runs fine... In both cases, a DataModel model is NOT created. I've included some code below.
This is what my code looks like:
var request = require('supertest');
var assert = require('assert');
var async = require('async');
var stubs = require('../stubs.js');
describe('DataModel', function() {
var testDataModel;
var dataModelParams = stubs.dataModelStub(); // simply returns a JSON dictionary
describe('#create()', function() {
describe('data model import', function() {
it('should import a new data model.', function (done) {
var agent = request.agent(sails.hooks.http.app);
agent
.post('/datamodel/create')
.send(dataModelParams)
.expect(302)
.end(function (err, res) {
if (err) {
throw new Error(err);
}
console.log(res.dataModel);
DataModel.find().exec(function (err, dataModels) {
console.log(dataModels); // should return an array of 1 model but returns empty array instead
done();
});
});
});
});
});
Snippet of my controller code:
create: function(req, res) {
DataModel.create(req.params.all(), function dataModelCreated(err, dataModel) {
if (err) {
sails.log.debug(err);
}
FlashService.success(req, 'Successfully imported a new data model.');
fs.ensureDirSync(path.join(sails.config.paths.DATASET_EXTRACT_PATH, dataModel.fileSafeName));
fs.ensureDirSync(path.join(sails.config.paths.DATASET_DOWNLOAD_ROOT, 'non_pii', dataModel.fileSafeName));
fs.ensureDirSync(path.join(sails.config.paths.DATASET_DOWNLOAD_ROOT, 'pii', dataModel.fileSafeName));
fs.ensureDirSync(path.join(sails.config.paths.DATASET_ENCRYPT_PATH, dataModel.fileSafeName));
return res.redirect('/admin/manage_data_models');
});
}
Note that the create function runs correctly in practice when my app is launched. Any suggestions as to why my test isn't working properly? I'm using sails-memory for the tests if that helps.
I figured it out. I needed to authenticate my agent first (by making a call to the login route) before any of these calls would make it through.
Essentially:
var agent = request.agent(sails.hooks.http.app);
agent.post('YOUR_LOGIN_ROUTE').end(done);
// do your tests
Hmm, don't you need to pass something that looks like the sails app to supertest? There's an example here that shows what you need to do. Look at the before function in the second answer:
How to test controller using mocha in Sails?

Difficulty testing asynchronous function

I'm trying to learn Mocha and am having trouble dealing with asynchronous code.
I would like to create a test database, populate it with some data, and then write some tests. My issue is that I can not figure out how to require that my test database be created and populated before additional tests are run. Here's the basic structure of my code:
describe('Database', function() {
// create the DB and populate it with some test data
iniDB(dbName, function(db) {
addDataToDB(db, table, data);
});
// tests below here
});
From reading the documentation, it seems that I need to use before to ensure that my asynchronous code runs prior to my tests. To attempt this, I tried using something like this:
describe('Database', function() {
before(function(done) {
iniDB(dbName, function(db) {
addDataToDB(db, table, data);
});
done();
});
// tests below here
});
How can I tell Mocha to first create and populate my database before running subsequent tests?
Using before() is the right solution, just invoke done() when all your data has finished writing. Something like this:
before(function(done) {
iniDB(dbName, function(db) {
addDataToDB(db, table, data, function() {
done();
});
});
});

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

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.

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