Nodejs - Mocha, Chai multiple async testing - node.js

Complete NodeJS testing noob here. Trying to individually test functions that are called through my API (meaning, rather than make an http request to a specific endpoint, which usually invokes several functions, which in turn make requests to different third party APIs, I want to test the functions themselves separately). The way they're called is I've built a class for each data source (data source = third party API), each class contains the same functions with the same exact signatures - getData and convertData, and return a callback with the results.
I've also created a module that creates many user mocks, since each user context returns different data (meaning, a user object is fed into getData, which uses certain user properties in order to determine what data should be returned).
The way I wanted to test this was to create numerous mocks, then run the functions for each. This is what I've got so far:
// Data sources to iterate over. Each is a class instance acquired through "require".
var dataSources = [
source1,
source2,
source3,
source4
];
describe('getData', function() {
this.timeout(10000);
describe('per data source,', function() {
context('standard call', function() {
// Associative array to hold the data returned, a key for each data source.
var finalResults = {};
// Iterate over all data sources
_.forEach(dataSources, function(dataSource) {
// Generate user mocks
var users = userMocks(10);
// Iterate over all users.
_.forEach(users, function (user) {
// Call each data source with each of the users.
// Numbers of calls to make - (users * data-sources), so in this case - 10*4.
dataSource.getData(user, function (err, data) {
if (err) return done(err);
// Convert the data returned to my format
dataSource.convertData(data, function (err, processedData) {
if (err) return done(err);
// Populate finalResults with converted data from each source
if (finalResults[dataSource.sourceName]) {
finalResults[dataSource.sourceName] = finalResults[dataSource.sourceName].concat(processedData);
} else {
finalResults[dataSource.sourceName] = processedData;
}
});
});
});
});
it('should return something', function(done) {
_.forEach(finalResults.keys, function(key) {
expect(finalResults[key]).to.not.be.empty;
expect(finalResults[key].length).to.be.greaterThan(0);
});
setTimeout(function() {
done();
}, 10000);
})
});
});
});
});`
This works (or at least the test passes when the query is valid, which is what I wanted), but it's cumbersome and (so very) far from elegant or effective, specifically the usage of timeout rather than using promises, async of some sort, or maybe a different alternative I'm not yet familiar with.
Since most of the resources I found (http://alanhollis.com/node-js-testing-a-node-js-api-with-mocha-async-and-should/, https://developmentnow.com/2015/02/05/make-your-node-js-api-bulletproof-how-to-test-with-mocha-chai-and-supertest/, https://justinbellamy.com/testing-async-code-with-mocha/, just to name a few) discuss direct API testing rather than specific async functions, I would love to get some input/best practices tips from more experienced Noders.

You need to know when bunch of asynchronous operations complete. Elegant way to test that is to use promises and promise aggregation:
Promise.all([ promise1, promise2, promise3 ]).then(function(results) {
// all my promises are fulfilled here, and results is an array of results
});
Wrap your dataSources into a promises using bluebird. You don't need to modify tested code self, bluebird provides convenience method:
var Promise = require('bluebird')
var dataSources = [
source1,
source2,
source3,
source4
].map(Promise.promisifyAll);
Use newly promisified functions to create promise for each call:
context('standard call', function() {
var finalResults = {};
var promiseOfResults = datasources.map(function(dataSource) {
var users = userMocks(10);
// Promise.all will take an array of promises and return a promise that is fulfilled then all of promises are
return Promise.all( users.map(function(user) {
// *Async functions are generated by bluebird, via Promise.promisifyAll
return dataSource.getDataAsync(user)
.then(dataSource.convertDataAsync)
.then(function(processedData) {
if (finalResults[dataSource.sourceName]) {
finalResults[dataSource.sourceName] = finalResults[dataSource.sourceName].concat(processedData);
} else {
finalResults[dataSource.sourceName] = processedData;
}
});
});
});
// promiseOfResults consists now of array of agregated promises
it('should return something', function(done) {
// Promise.all agregates all od your 'datasource' promises and is fulfilled when all of them are
// You don't need the promise result here, since you agegated finalResults yourself
return Promise.all( promiseOfResults ).then(function() {
_.forEach(finalResults.keys, function(key) {
expect(finalResults[key]).to.not.be.empty;
expect(finalResults[key].length).to.be.greaterThan(0);
});
done();
});
});
Rest of your test should use same Promise.all( promiseOfResults ), unless you need new set of results.

Related

MongoDB with node.js - how to return data async

I've tried a few approaches to this - what I have is all my modules in one file that interact with mongodb, and in another, the express route functions that call into those async functions looking for data. The problem is that the data is available in the async function, but is not returned to the calling function, and I'm not sure how to pass it back properly (not sure if it's an issue of not waiting for the async function to return and returning the array early, or if I'm actually returning it wrong).
Here is the code for the calling function
router.route('/').get((req, res) => {
db.getItemsFromCollection("Plants").then( (itemArr) => {
console.log(itemArr);
})
});
And the db function (two attempts, one commented out)
getItemsFromCollection: async function(collectionName) {
let itemArr = [];
const collection = client.db().collection(collectionName);
/*collection.find().forEach(function( doc) {
itemArr.push(doc);
//console.log(doc);
})
return itemArr;*/
collection.find({}).toArray(function(err, item) {
if (err) throw err;
console.log(item);
itemArr.push(item);
})
return itemArr;
},
If you are using mongodb then it already supports Promise syntax:
However, all async API calls support an optional callback as the final
argument, if a callback is provided a Promise will not be returned.
Then you can make getItemsFromCollection become:
getItemsFromCollection: async function (collectionName) {
// let itemArr = [];
const collection = client.db().collection(collectionName);
const items = await collection.find({}).toArray(); // don't pass callback param
console.log(items);
// I guess you want to get back an array of the collection item
return items;
},

How to get Entities synchronously while using Nodejs

I'm trying to get storage account's Table's data.
I succeed in getting the date using the way here.
But it's using callback. I want to get the results synchronously!
You can write a helper function which returns Promise to make it synchronous (or simulate it)
function getSome(mytable, hometasks, num)
return new Promise((resolve, reject) => {
tableSvc.retrieveEntity(mytable, hometasks, num, function(error, result, response){
if(!error){
resolve(entity // result or response etc)
} else {
reject(error)
}
});
})
Then you can use elsewhere in your code with async/await (to pause execution) like
Note you can use await only inside async function
async function useData() {
const data = await getSome('mytable', 'hometasks', '1');
// use data here
}
or with plain promise as (Note, this does not pause execution, code inside then is a callback function again)
const data = getSome('mytable', 'hometasks', '1');
data.then(res => // do something)
Also looks like cosmos have now sdk with Promise support.
Read more about Promise and async/await on MDN

Node.js consecutive method calls with nested callback formatting

So I'm new to Node.js and Im just wondering if the way I have my code setup makes sense. Im coming from a Java background so the nested callback structure is new. I have a Node program that runs a bunch of code that I broke down into different methods. The thing is that the methods need to be called in order. My code has this structure right now:
functionOne(data, callback(err) {
functionTwo(data, callback(err) {
functionThree(data, callback(err) {
functionFour(data, callback(err) {
//Code
});
});
});
});
This is very minimalistic, but is this structure ok? With Java, I'd take the return values of all the methods, then just pass them to the next function. From my understanding so far, the Java approach I just mentioned is one of the main things that Node.js was trying to eliminate. But anyway... Does that structure look ok, and is that how its intended to look? Just want to be sure that I'm not making any major errors with Node in general. Thanks!
Your code structure looks fine if you work with callback pattern.
But if you're interested in make your code cleaner and readable you would like to use Promises in your asynchronous function, so instead of pass a callback to your functions you could do something like this :
function asyncFunction (data){
return new Promise(function(resolve, reject){
// Do something with data
// Here you can call reject(error) to throw an error
resolve();
});
}
And instead of nested function callbacks you can call then method of Promise.
asyncFunction(data)
.then(function(){
// Promise resolved
// Something has been done with data
});
With Promises you can also execute async fuctions in parallel :
Promise.all([asyncFunctionA(data), asyncFunctionB(data), asyncFunctionC(data)])
.then(function(){...});
EDIT
If you need to pass values of one function to another, your code should look like this :
asyncFunctionA(data)
.then(function(dataA){
return asyncFunctionB(dataA);
})
.then(function(dataB){
return asyncFunctionC(dataB);
})
.then(function(dataC){
// ...
});
You should try to use promises to avoid your callback hell, so it could be something like these...
const Q = require('q'); // you can do a research for this module.
var myModule = {};
myModule.functionOne = (params) => {
const deferred = Q.defer(); // wait for this to complete
// body function
deferred.resolve(data); // this would be the result of this function
return deferred.promise; // data is the output on your function
}
myModule.functionTwo = (params) => {
const deferred = Q.defer(); // wait for this to complete
// body function
deferred.resolve(data); // this would be the result of this function
return deferred.promise; // data is the output on your function
}
myModule.doAll = (params) => {
myModule.functionOne(params)
.then((outputFunctionOne) => {
// this is called after functionOne ends
return myModule.functionTwo(outputFunctionOne);
})
.then((outputFunctionTwo) => {
// this is called after function 2 ends
if (outputFunctionTwo.success) {
// if everything ok, resolve the promise with the final output
deferred.resolve(outputFunctionTwo);
} else {
// reject the promise with an error message
deferred.reject('error');
}
})
.fail((err) => {
// this is call if the promise is rejected or an exception is thrown
console.log(err); // TODO: Error handling
})
.done();
}
module.exports = myModule;
You can Chain as many promises as you want really easily, that way you get rid of the callback hell. Best part, you can do promises on Javascript or Node.js
Reference Link https://github.com/kriskowal/q
Hope this helps
Most of the other answers give Promise/A as the answer to your callback woes. This is correct, and will work for you. However I'd like to give you another option, if you are willing to drop javascript as your working language.
Introducing Iced Coffee, a branch of the CoffeeScript project.
With Iced Coffee you would write:
await functionOne data, defer err
await functionTwo data, defer err2
await functionThree data, defer err3
//etc
This then compiles to the CoffeeScript:
functionOne data, (err) ->
functionTwo data, (err2) ->
functionThree data, (err3) ->
//etc
Which then compiles to your Javascript.
functionOne(data, callback(err) {
functionTwo(data, callback(err2) {
functionThree(data, callback(err3) {
//etc
});
});
});

Using a generator to call an API multiple times and only resolve when all requests are finished?

I'm making a simple NodeJS app and I'm refactoring it out of my callback hell.
I've realised generators could be used but I'm struggling to grasp exactly how to use them.
Here's the basic flow of my function (I'm using the request-promise module):
// Iterate through keys to get values for
Object.keys(sourceData).forEach(function(key){
makeRequest(key);
})
makeRequest is a function that basically does this (it's incomplete):
// Make Request
function makeRequest(key) {
rp(apiEndpoint)
.then((data) => {
staticDictionary[key] = data.value;
})
}
I want to synchronously make a call to the endpoint, wait until it's finished getting the data, then move on to the next key in the loop using generators.
Can someone help?
You can sequence your requests one after the other, using .reduce() and promises.
// Make Request
function makeRequest(key) {
// use the key to construct the apiEndpoint here
return rp(apiEndpoint).then((data) => {
return data.value;
});
}
Object.keys(sourceData).reduce(function(p, key){
p = p.then(function(dictionary) {
return makeRequest(key).then(function(data) {
dictionary[key] = data;
return dictionary;
});
}
}, Promise.resolve({})).then(function(dictionary) {
// dictonary contains all your keys here
});
But, since none of your requests depend upon the prior requests, you could also run them all in parallel:
// Make Request
function makeRequest(key) {
// use the key to construct the apiEndpoint here
return rp(apiEndpoint).then((data) => {
return data.value;
});
}
// Iterate through keys to get values for
var staticDictionary = {};
Promise.all(Object.keys(sourceData).map(function(key){
return makeRequest(key).then(function(data) {
staticDictionary[key] = data;
});
})).then(function() {
// staticDictionary is fully available here
});
Using the Bluebird promise library, you could use Promise.props() which takes an object with promises as values and returns an object with the same keys, but resolved values as values (you pass in a map object and get back a map object):
// Make Request
function makeRequest(key) {
// use the key to construct the apiEndpoint here
return rp(apiEndpoint).then((data) => {
return data.value;
});
}
var promiseMap = {};
Object.keys(sourceData).forEach(function(key) {
promiseMap[key] = makeRequest(key);
})
return Promise.props(promiseMap).then(function(dictionary) {
// dictionary is available here
});
You can use Array.reduce function to do so.
Object.keys(sourceData).reduce(function(results, key){
makeRequest(key)
.then(function(data) {
results[key] = data;
});
}, {});
You can take a look at Bluebird, it has a lot of features related to what you are trying to acomplish.
Checkout co
co(function*(){
// Iterate through keys to get values for
for(const key in Object.keys(sourceData)){
// yield the promise
yield makeRequest(key);
}
});
// Make Request
function makeRequest(key) {
// return the promise
return rp(apiEndpoint)
.then((data) => {
staticDictionary[key] = data.value;
});
}
A better way is figuring out how does the ES6 generator work and probably do the things by yourself. Although co is a good library. :)

Testing Express and Mongoose with Mocha

I'm trying to test my REST API endpoint handlers using Mocha and Chai, the application was built using Express and Mongoose. My handlers are mostly of the form:
var handler = function (req, res, next) {
// Process the request, prepare the variables
// Call a Mongoose function
Model.operation({'search': 'items'}, function(err, results) {
// Process the results, send call next(err) if necessary
// Return the object or objects
return res.send(results)
}
}
For example:
auth.getUser = function (req, res, next) {
// Find the requested user
User.findById(req.params.id, function (err, user) {
// If there is an error, cascade down
if (err) {
return next(err);
}
// If the user was not found, return 404
else if (!user) {
return res.status(404).send('The user could not be found');
}
// If the user was found
else {
// Remove the password
user = user.toObject();
delete user.password;
// If the user is not the authenticated user, remove the email
if (!(req.isAuthenticated() && (req.user.username === user.username))) {
delete user.email;
}
// Return the user
return res.send(user);
}
});
};
The problem with this is that the function returns as it calls the Mongoose method and test cases like this:
it('Should create a user', function () {
auth.createUser(request, response);
var data = JSON.parse(response._getData());
data.username.should.equal('some_user');
});
never pass as the function is returning before doing anything. Mongoose is mocked using Mockgoose and the request and response objects are mocked with Express-Mocks-HTTP.
While using superagent and other request libraries is fairly common, I would prefer to test the functions in isolation, instead of testing the whole framework.
Is there a way to make the test wait before evaluating the should statements without changing the code I'm testing to return promises?
You should use an asynchronous version of the test, by providing a function with a done argument to it.
For more details refer to http://mochajs.org/#asynchronous-code.
Since you don't want to modify your code, one way to do that could be by using setTimeout in the test to wait before to call done.
I would try something like this:
it('Should create a user', function (done) {
auth.createUser(request, response);
setTimeout(function(){
var data = JSON.parse(response._getData());
data.username.should.equal('some_user');
done();
}, 1000); // waiting one second to perform the test
});
(There might be better way)
Apparently, express-mocks-http was abandoned a while ago and the new code is under node-mocks-http. Using this new library it is possible to do what I was asking for using events. It's not documented but looking at the code you can figure it out.
When creating the response object you have to pass the EventEmitter object:
var EventEmitter = require('events').EventEmitter;
var response = NodeMocks.createResponse({eventEmitter: EventEmitter});
Then, on the test, you add a listener to the event 'end' or 'send' as both of them are triggered when the call to res.send. 'end' covers more than 'send', in case you have calls other than res.send (for example, res.status(404).end().
The test would look something like this:
it('Should return the user after creation', function (done) {
auth.createUser(request, response);
response.on('send', function () {
var data = response._getData();
data.username.should.equal('someone');
data.email.should.equal('asdf2#asdf.com');
done();
});
});

Resources