Mocha/Chai tests hanging at .should.be.deep.equal despite result existing - node.js

I'm running some tests using Mocha and chai. One of the tests is hanging at a .should.be.deep.equal call.
Here's the test code:
// Make the fake connection before running tests
before(function(done) {
mongoose.connect('mongodb://fake.test/TestingDB', function(err) {
done(err);
});
});
// Test Cases
describe('Testing the functions that deal with users and locations:', function() {
// Test Setup
var req = {};
beforeEach(function(done) {
mockgoose.reset()
async.parallel([function(callback){
sensors.create(testData.deviceData, function(err, model) {
if (err) {console.log(err)}
callback();
});
}, function(callback) {
locations.create(testData.locationData, function(err, model) {
if (err) {console.log(err)}
callback();
});
}], function(err) {
done();
});
});
afterEach(function(done) {
mockgoose.reset();
done();
});
// Tests
describe('function locationList', function() {
it('should list the test location', function(done) {
dbFunctions.locationList(req, function(result) {
console.log(result) //Prints the whole result
console.log(testData.locationList)
result.should.exist; //This doesn't cause it to hang
result.should.be.deep.equal(testData.locationList) //hangs here
done(result);
});
})
})
});
And here's the function it's testing:
exports.locationList = function(req, callback) {
listLocations().then(
function(data) {
callback(data);
},
function(err) {
console.log('Error Retrieving Location Information: ' + err);
callback(err);
});
};
As I note in the comments, the results object exists and gets printed to the console. results.should.exist; doesn't throw an exception and if I comment out everything but it the test works fine. For some weird reason, despite both the testData.locationList and result object existing, the test times out. I have 14 other tests that use the exact same syntax without any problems. Does anyone know what could be causing this to happen for this specific test?
Here's the output from the tests:
Testing the functions that deal with users and locations:
function locationList
[ { devices: {},
address: '123 Fake St, Waterloo, On',
location: 'Unittest',
owner: 'unit#test.com',
_id: '-1' } ]
[ { devices: {},
address: '123 Fake St, Waterloo, On',
location: 'Unittest',
owner: 'unit#test.com',
_id: '-1' } ]
1) should list the test location
0 passing (2s)
1 failing
1) Testing the functions that deal with users and locations: function locationList should list the test location:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
at null.<anonymous> (C:\Users\My Name\AppData\Roaming\npm\node_modules\mocha\lib\runnable.js:189:19)
Extending the timeout doesn't work. Nor does putting something random (ie. the integer 1 in the .should.be.deep.equal() function.

My guess is that the callback in exports.locationList is called synchronously, and that your test case is actually failing, throwing an exception, which never gets caught because the callback is (synchronously) called from within a promise handler (more info here).
Try and see if this works better:
dbFunctions.locationList(req, function(result) {
setImmediate(function() {
console.log(result) //Prints the whole result
console.log(testData.locationList)
result.should.exist; //This doesn't cause it to hang
result.should.be.deep.equal(testData.locationList) //hangs here
done(result);
});
});
// or use a module like dezalgo (https://github.com/npm/dezalgo)
The underlying cause may be mockgoose.
Also, you're not using the proper Node.js conventions where the first argument to a callback function is "reserved" for errors. In other words, your code should look similar to this:
if (err) {
callback(err);
} else {
callback(null, data);
}
You're now passing both errors and data as the first argument.

A guess: Maybe the issue lies with mongoose decorating the result with it's own functions/members, and one of them somehow getting stuck in an infinite loop when chai tries to enumerate them all to perform a deep comparison.

Related

how can I test fail error function with chai and mocha

Hello guys i'm newbie with testing
I'm using socket.io and I want to simulate throwing error by my function when something happens on the insertion.
socket.on('request', function(request) {
bookingService.addBooking(request)
.then(function (booking) {
winston.info('Client Order saved');
io.emit('order to driver', {user: request[2], random : request[3]});
})
.catch(function (err) {
winston.error(' Client error on save order ==> '+err);
});
});
addBooking
function addBooking(msgParam) {
var deferred = Q.defer();
db.booking.insert(
{ user : msgParam[2],
adress : msgParam[0],
random : msgParam[3],
driver : [],
isTaken : false,
isDone : false,
declineNbr : 0 ,
createdOn : msgParam[1],
createdBy : msgParam[2],
updatedOn : null,
updatedBy : []},
function (err, doc) {
if (err) deferred.reject(err.name + ': ' + err.message);
deferred.resolve();
});
return deferred.promise;
}
I tried to just test the addBokking function
it('should throw error if something wrong happend on adding new order ', function(done){
(bookingService.addBooking(request)).should.throw()
done();
});
but I get this error
AssertionError: expected { state: 'pending' } to be a function
You can use the following syntax with chai:
it("throw test", () => {
expect(()=>{myMethodThatWillThrowError()}).to.throw();
});
For promises, you can use the following pattern:
it("should throw on unsuccessfull request", (done: MochaDone) => {
repo.patch({
idOrPath: "Root/Sites/Default_Site",
content: ConstantContent.PORTAL_ROOT,
}).then(() => {
done("Should throw"); // The test will fail with the "Should throw" error
}).catch(() => {
done(); // The test will report success
});
});
You are checking the promise and not the result
This error:
AssertionError: expected { state: 'pending' } to be a function
... means you are checking the promise returned from the addBooking function and not the resolved/rejected result of the promise.
With chai-as-promised you can do that easily!
With chai-as-promised these should work for example (from the documentation):
return promise.should.be.rejected;
return promise.should.be.rejectedWith(Error); // other variants of Chai's `throw` assertion work too.
or in your specific case (after installing and connecting chai-as-promised), this should work:
(bookingService.addBooking(request)).should.be.rejected
(maybe should.throw() will work with chai-as-promised too, I'm less familiar with it)
Check it out here: chai-as-promised

async.<fn>Limit stop after first iteraton loop

this question is related with an answer to my previous question. there #robertklep recommends me to use mapLimit() instead of .map() because .map() can't handle a large series of data, and with that solution all works fine. But now I restructured my code, and now neither of the .<fn>Limit() functions run after the first loop iteration. do I missing something here?
var proccesBook = function(file, cb) {
testFile(file, function (epub) {
if (epub) {
getEpuData(file, function (data) {
insertBookInDB(data)
})
}else{
cb(file)
}
})
}
async.mapLimit(full_files_path, 10, proccesBook, function(err){
if(err){
console.log('Corrupted file', err);
} else {
console.log('Processing complete');
};
})
// ---> only runs for the first 10 series data
Your primary issue is you don't call cb in the success branch of processBook. Your control flow must guarantee to call the callback exactly once for each worker function invocation.
Other asides:
You don't seem to need the results, so eachLimit is fine
Only need mapLimit if you need the results of each worker
You need to follow the standard error-first convention when calling the callback. Don't do cb(file) as that will be interpretted as an error and about the remaining processing.
var proccesBook = function(file, cb) {
testFile(file, function (epub) {
if (epub) {
getEpuData(file, function (data) {
insertBookInDB(data)
cb() // This is what you were missing
})
}else{
cb()
}
})
}
async.eachlimit(full_files_path, 10, proccesBook, function(err){
if(err){
console.log('Corrupted file', err);
} else {
console.log('Processing complete');
};
})

unit testing async-waterfall using mocha

I am using Mocha as a testing framework for my node.js application.
I have an existing module that uses async-waterfall node module.I am trying to write a unit test case for it. I am unable to write a test case for the second function in the array. Any tips on how would I pass the result of one function to the next function in the array
var async = require('async');
module.exports.resetPassword = function(req,res,next){
async.waterfall([
function(done){
var token = do something;
done(err,token);
},
function(token,done){
//do some stuff
res.status(200).send('success');
},
]function(err){
//send failure response
});
};
I think I understand the issue now.
In your test - you execute the line:
user.resetPassword(request,response,next);
And immediately after that - the line:
cryptoStub.restore();
The issue is - that async.waterfall will start the first method, but before it runs the second one, it will run the next line from the unit test, and therefore you don`t see the next method. Code that will solve it will be like:
user.resetPassword(request,response,function(err) {
cryptoStub.restore();
done();
});
Does it look better?
My code is as follows. My issue is around writing a unit test case for this method. I am using mocha along with sinon for stubbing. The unit test that I have written is
Unit Test
it("Should return an error if there is an error in retrieving the user details from the db",function(done)
{
request={
body:{
email:'test#test.com'
}
};
var expectedMessageFromCrypto = '12121212121';
var cryptoStub = sinon.stub(crypto, "randomBytes",function(err,callback){
callback(null,expectedMessageFromCrypto);
});
user.resetPassword(request,response,next);
cryptoStub.restore();
done();
});
});
The resetPassword module
methods.exports.resetPassword = function (req, res, next) {
var token = null;
async.waterfall([
function (done) {
crypto.randomBytes(16, function (err, buf) {
if(err){
return res.status(400);
}
else{
token = buf.toString('hex');
}
done(err, token);
});
},
function (token, done) {
var user = getUserInformation(req.body.email);
sendEmail(user, done);
}
], function (err) {
if (err) {
logger.error('error in sending confirmation mail');
return next(err);
}
res.status(200).send('success');
});
};

Mocha/Chai Tests using Mongoose Occasionally Fail on First Run

I have a series of Mocha/Chai tests that are set up as follows:
var mongoTest = require('../mongoTest.js');
//Connect to test DB before tests and disconnect after
before(function(done) {
mongoTest.mongoConnect(done);
});
after(function(done) {
mongoose.disconnect(done);
})
//Load Data Files
var testData = require('../testData.js')
var deviceAndLocationAnswers = testData.deviceAndLocationAnswers
//Repeated Functions:
var clearCollections = function(coll, callback) {
mongoose.connection.db.dropCollection(coll.collectionName,
function(err, result) {
callback();
});
}
describe('Testing functions that use the Breakers collections', function(){
//Test Setup
var req = {query: {device: testData.powerConsumptionDevice}}
before(function(done) {
this.timeout(15000);
async.forEach(mongoose.connection.collections, clearCollections,
function(err) {
if (err) {console.log(err)};
done();
})
});
before(function(done) {
this.timeout(15000);
Breakers.create(testData.breakersData, function(err, model){
done(err);
});
});
after(function(done) {
this.timeout(15000);
async.forEach(mongoose.connection.collections, clearCollections,
function(err) {
if (err) {console.log(err)};
done();
})
});
// Tests
describe('Testing powerConsumption Function', function() {
it('Should produce some output', function(done) {
this.timeout(15000);
dbFunctions.powerConsumption(req, function(result) {
result.should.exist;
done();
});
});
it('Should produce the same results as the mock up from testData', function(done) {
this.timeout(15000);
dbFunctions.powerConsumption(req, function(result) {
result.should.be.deep.equal(testData.powerConsumptionResults);
done();
});
});
});
});
mongoTest comes from the following file I have:
var mongoose = require('mongoose')
var dBaseURL = 'mongodb://xxxx:yyyyy#ds#####.mongolab.com:zzzz/myDB'; // details removed
exports.mongoConnect = function(callback) {
mongoose.connect(dBaseURL, function(err) {
if(err) {
console.log('MongoDB Connection Error', err);
} else {
console.log('MongoDB Connection Successful')
}
callback();
});
};
I have a total of 14 tests, set up similarly to the ones I've used as examples. The first time I run these tests, a few will always fail (it's never the same few). If I run the tests again immediately after, all of them will pass.
Failure takes the form of the .should.be.deep.equal() calls failing with large diffs. I'm not sure how this could happen, as the data doesn't change between tests.
I've checked the database between tests and the collections are always deleted after the tests have run.
Does anyone have any ideas as to what could be going on here? I'm new to node.js, so it's very likely I've missed some bit of best practice that's causing all of this.
Also, I am aware that it is best practice to mock out the database. I already have a set of tests that do this. I also want a set of tests that use the database so that I can test the actual behavior.
ETA:
Turns out my problems had nothing to do with the code I had posted here.
The functions that would occasionally fail the tests were those that had database calls that didn't sort the output in any particular way. It turns out that Mongoose would sometimes (but not always? WHY?) sort the query results in such a way that they would pass the test. I'd be interested to hear an explanation of how this is the case, but my question can be considered solved.

Jasmine: how to run series of tests after an event has triggered

I'm trying to run tests on my npm module. The code below will work if I comment out either of the 2 it blocks, but times out if I leave them both in as below. How can wait for "ready" before running my tests (I'd like to add more but they too need to await "ready")?
describe("GA analytics", function() {
var report = new Report(private_key, SERVICE_EMAIL, 1);
it("should connect and emit <ready>", function(done) {
report.on('ready', function() {
console.log("test.js: Token: ", report.token);
expect(report.token).toBeDefined();
done();
});
});
it("should get data correctly", function(done) {
report.on('ready', function() {
report.get(query, function(err, data) {
if (err) throw err
expect(data.rows).toEqual([ [ '5140' ] ]);
done();
});
});
});
});
I guess it is happening because you create a new instance of the Report only once per test file, therefore the ready event only fires once and only the first it block in the test will catch it and process. The rest of the it blocks won't receive any more ready events, so they silently wait for it until Jasmine times out. The solution will be to create a new Report instance before each it block, which can be easily done with the help of Jasmine beforeEach:
describe("GA analytics", function() {
var report;
beforeEach(function () {
report = new Report();
});
// ....
});
See the working example here on Plunker (open a "script.js" file)

Resources