Test callback using in a route with supertest - javascript

I started to istanbul as a test coverage tool with mocha and one of the great things is that it shows you the paths (branches) you have tested in your test code logic.
There is a path that can only be taken if a error occurs on the database.
Screenshot of the part I am interested in testing
The [I] indicates the the first if was not tested.
The problem is that it uses a callback function(err, data) and the error is passed to this callback through a the mongoose model method find(), and because of that I don't have the flow control of this part of the code.
In this specific case I using supertest which is a module to test routes in node.js and it make requests to a route that calls a mongoose model method find().
What would be the best option to test this path? Create a stub to simulate the method? Or just remove the if?
EDIT: I noticed that I was using an anonymous function as a callback (err, data) and doing so I can't test it since it's not exposed to the outer scope. One approach I had in mind was to create a function:
handleDbFetchingResponse(res) {
return function(err, data) {
let response = {};
if (err) {
response = {error: true, message: 'Error fetching data'};
} else {
response = {error: false, message: data};
}
res.json(response);
}
}
Now I can expose the function and test it, I create another problem though. Since the other express routes have another logic when fetching data from the database I will have to create a handler function for each one of them. Maybe there is a way to create a handlerBuilder function that returns a new handler passing different arguments to deal with specific cases.

Related

Unit Testing - Mock Methods within Methods?

I am writing unit tests for a Node.js application, and I am wondering if I am mocking the correct parts of the code.
The example below is a hypothetical class that has two static methods.
The method isTokenValid calls another method, decodeToken which takes the token and a callback. The callback is defined inside of isTokenValid. Both these methods belong to the same class.
When unit testing isTokenValid my approach is to mock the decodeToken method.
It is clear to me that when unit testing, dependencies such AJAX requests should be mocked. However, does that also hold true for this type of dependency or am I being too granular?
Is mocking decodeToken the right approach to unit testing isTokenValid?
var TokenClass = {};
TokenClass.isTokenValid(token) {
TokenClass.decodeToken(token, function(err, decoded) {
if (err) {
console.log('There was a validation error');
}
if (decoded) {
return true
};
}
}
TokenClass.decodeToken(token, callback) {
// some logic here to decode token
if (err) {
return callback(err);
}
// if token is not valid
if (!validToken) {
return callback(null, undefined);
}
// if token is valid
return callback(null, decoded);
}
}
There are two approaches.
In classic unit tests you can mock everything that is external to your tested unit - in this case isTokenValid method is your unit. But that approach isn't practical.
The best way is to mock things that doesn't let your tests run in isolation and in deterministic way (same result every time).
If decodeToken is not calling any external resource (url, database, file system) then you don't have to mock that out. However if it does call external resource, then decodeToken should be implemented in another object, i.e. TokenDecoder and injected into TokenValidator, then for unit test of TokenValidator you can inject mocked TokenDecoder that is not calling any external resource.
TokenDecoder then should be tested using integration test, but that is another topic.

Sailsjs create objects - nested creations

I have a controller which accepts an API call /task/:id/start. My controller method needs to check if Task with at id is valid and if that's valid then I need to create 2 other model instances. I need to create TaskSet and then TaskSetEvents.
TaskSet requires task to be created and TaskSetEvents requires TaskSet to be created. Here is how I'm planning on creating these events. I'm not sure if there is a better way of creating these objects.
TaskSet.create({ task: task}).exec(function(err, taskSet) {
TaskSetEvent.create({ taskSet: taskSet, eventType: 'start'}).exec(function (err, taskSetEvent) {
console.log("Everything created ok");
});
});
This should just work:
TaskSetEvent.create({
eventType: 'start',
taskSet: {
task: myTask
}
})
.then(function (taskSetEvent) {
console.log('should be done here');
});
If you're doing this through a controller endpoint, you shouldn't have to write any code. Just POST your nested object.
Everything is fine with your code. Anyway, when there are more nested functions code becomes hard to read and maintain, something called spaghetti code or callback hell.
In JavaScript common ways of solving callback problem are using promises or using special tools, like async.js.
For your code snippet async.waterfall() is definite way to go. You can rewrite it in such way:
async.waterfall([
function(cb) {
TaskSet.create({ task: task}).exec(cb);
},
function(err, taskSet, cb) {
TaskSetEvent.create({ taskSet: taskSet, eventType: 'start'}).exec(cb);
}
], function(err, taskSetEvent) {
console.log('Everything created ok');
});
waterfall method runs series of functions each passing the results to the next.
Not worth saying that if you want to use async frequently, it is not necessary to require it each time in your modules, you can just install it via npm and save async: true in your globals config.

What are the 'req' and 'res' parameters in node and node middleware?

I'm new to Node and Express and the other layers that go along with building web apps with node and the request and response parameters are really confusing me. My confusion lies in the fact that those two parameters are often present in a function but oftentimes one or both of them isn't declared. Also, much of the time an additional parameter will be thrown in, like 'next' or something else. For example, I have the following router for an API:
router.route('/teams')
// create a team at POST http://localhost:8080/api/teams
.post(function(req, res) {
var team = new Team();
team.name = req.body.name;
team.location = req.body.location;
// save the new team and check for errors
team.save(function(err) {
if (err) {
res.send(err);
};
res.json({ message: 'Team created!' });
});
})
// GET all the teams at GET http://localhost:8080/api/teams
.get(function(req, res) {
Team.find(function(err, teams){
if (err) {
res.send(err);
};
res.json(teams);
});
});
Both .post and .get call a function with req and res as parameters, but req is never used. So how does the function know what to do with req or res if they're not defined and used or not used in completely different orders? Or if I named them something completely different?
What exactly is happening with requests and responses? Sorry for my ignorance. I've read the documentation but it's not clicking.
Thanks.
When you use expressApp.use('/some/route', myRouteHandler); Express will listen for requests for that route, and when it's hit, it will call the function you provided (callback). It will give it three parameters: request and response, and next. (Actually could be four, but lets keep things simple.)
So, your callback might be defined like this:
function myRouteHandler(a, b, c) {
// do stuff
};
or like this:
function myRouteHandler(req, res, next) {
// stuff
}
or simply:
function myRouteHandler() {
// stuff
}
Whatever you do, doesn't matter. When the app is started, express listens for requests.
When one of them matches the route (/some/route), express will, in its internal workings, call the function you provided, like this:
myRouteHandler(requestObject, responseObject, nextMiddleware);
So in the first case, you can access the request (like, request headers, full url, caller IP address or similar) by using req. In your second case, you'll access it by calling a. In the third case, you can use arguments[0].
By convention, people will use the form: myCallback(req, res) and know that Express will put the request object as the first param, and response as the second. The response object actually has a method end(), so you can end the request. If there is also a next() object, you can call the next middleware.
Say you have a route defined like this:
app.use('/api/users', checkAuthorizationHandler);
app.use('/api/users', makeSureTheIPisFromOurInternalNetwork);
app.use('/api/users', nowHandleTheResponse);
Each of those handlers gets a third param. If you name it, you'd usually in your function declaration call it 'next' parameter. It means, the next function in order.
Say your function checkAuthorizationHandler(req, res, next) will check for req.headers('auth') token and if it's ok, it will in the function body, call next().
Then the function makeSureTheIPisFromOurInternalNetwork(a, b, c) is called. It will check that the a.ip is a LAN ip address and call c();
Finally your function nowHandleTheResponse() will find all users, and respond with a JSON object of the users: arguments[1].json([user1, user2, user3]);
So, first param is something that express gives you, it's the request, second is response, third is a next middleware function in line. No matter what you call them, they are there.
P.S. You can also declare your middleware with four params:
function(error, req, res, next);
Express will actually check your function and if it finds that you have four params and not two or three, it will give you any errors thrown by the middleware that ran earlier in the chain. Meaning, if your checkAuthHandler says next(new Error('Not authorized'));, your next function might check for that error, and if it's present, not give results. Often however will the middleware which detects errors just res.end('some error message');
If I haven't confused you enough, just say, I've got more where this came from :)
It is the framework convention. The first argument is the request, the second is the response. If you're declaring a middleware (.use), the third argument is the next middleware in the chain.
You can name these variables however you want, as long as you know the order. You could have something like: .post(function(a,b) {}); and then the request is represented by variable a, and response by variable b.
If, for whatever reason, you don't need the request, only the response, you still have to have a first argument, as the response is represented by the second argument.
In javascript, there's no method overload like in Java, for example (maybe here's where you getting the confusion from). A function is represented by its name, not how many arguments it takes. Here's a simple example:
function logToConsole(level, message) {
if (!message) {
message = level;
level = 'INFO';
}
console.log('['+level+']', message);
}
logToConsole('My message'); // prints out: "[INFO] My message"
logToConsole('WARN', 'My message'); // prints out: "[WARN] My message"
Did you notice how we defined a default value for level, based on the existence of message?
Hope this clarifies things a bit.
Request, response and next are passed to all middleware functions. The request object contains information about the HTTP request, and the response object is used to handle the request. The Expressjs documentation details these objects. The next() call is used in something called the dispatcher, a middleware function may or may not call next() depending on usage. Next simply calls the following middleware.
Here is an example of using next():
function helloWorld(req,res,next){
console.log('Hello, world!');
next();
}
// This function doesn't call next(), therefore it will
// not call the subsequent middleware in the dispatcher
function goodbyeWorld(req,res,next){
console.log('Goodbye, world!');
}
app.use(helloWorld);
app.use(goodbyeWorld);
Output:
Hello, world!
Goodbye, world!
Now let's reorder the middleware
app.use(goodbyeWorld);
app.use(helloWorld);
Output:
Goodbye, world!
The helloWorld function is not called. Notice the importance of middleware order and the next() function call.

How can I unit test this code snippet?

I've just recently moved to a new project that deals mainly in Javascript (as a Node.js web application).
I'm a fairly TDD focused developer, and am trying to figure out the best approaches / patterns to ensure that what we end up building is unit-testable and maintainable.
I've been trying to wrap the following code snippet with unit tests, but am having trouble getting good code coverage over the anonymous function passed in as the request callback.
I have mocked the request object using the rewire.js library, and can successfully test that the logger was called, that request was called with the correct parameters, but how do I complete the test coverage for this?
function _makeRequest(apiName, options, payload, callback) {
logger.info('DS API %s Request:\n %s %s\n %s', apiName, options.method, options.url, logger.look(payload));
request(options, function(error, response, body) {
var json = 'json' in options ? body : JSON.parse(body);
if ('error' in json) {
var msg = 'DS API ' + apiName + ' Error:\n ' + logger.look(json.error);
logger.info(msg);
callback(null);
} else { // no error
logger.info('DS API %s Response:\n %s', apiName, logger.look(json));
callback(json);
}
});
}
Should I be refactoring for better testability? Is there a common approach for unit testing callbacks that I'm not aware of?
Carl put me on the right direction. I had set up my parameters for the tests with a good range of input data (to ensure that all code lines would be executed in one test or another) but, in the end, was failing to actually execute the callback parameter after passing it to the rewire.js Mock.
The callback was making it in, but I needed to execute it from within the mock to ensure that the callback code would still be executed

Node best practices: Throwing async error in constructor

I am working with Node and I have a "class" that takes a directory as a parameter. It tries to create that directory and if it fails, then it throws an error:
function Config(dir) {
fs.mkdir(dir, function(err) {
if(err) throw new Error('Error', err);
}
}
My question is, is this an approved way of doing this? If I were to use a callback, then the rest of my program would have to reside in that callback, which seems odd to me.
This issue manifested itself when I tried to write a test using mocha which won't work since the exception is thrown in an async call:
it('should throw an error on a bad directory', function() {
var fn = function() {
var badConfig = new Config('/asdf');
};
assert.throws(fn, Error);
});
I've investigated domains as a way to solve the unit test issue, but that didn't seem to solve my problem (or I didn't implement them correctly).
var d = domain.create().on('error', function(err) { throw err; }
d.run(function() {
function Config(dir) {
fs.mkdir(dir, function(err) {
if(err) throw err;
}
}
});
Ultimately, I'm looking for a best practice that allows me to indicate to the application that something bad happened, and allows me to create tests for that solution.
You have three possibilities:
Using a synchronous call. As AsolBerg explained, your case suits exactly why some fs functions have their synchronous equivalent. It's ok because in your case, all your application depends on one Config instance to be loaded. but there are cases
Using a callback as constructor argument.
If constructor callback sounds really too odd for you, put your initialization code into an init() method, that takes a callback. It's a matter of personnal preference, but rather use this technic.
Last option, you can returns a Future in your init() method. There are several future libraries in NodeJS, that are an elegant alternative to callback parameter. But you can't use it in your constructor... as the constructor's return is the created object.
It sounds like in this case you might actually want to make a synchronous call (e.g. the rest of your application depends on this call being finished before proceeding). So although its normally not the way you want to think about building your node apps you could use the synchronous version mkdirSync().
http://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_mode
Then if the call fails you can catch the error and return it and (probably) exit the app.

Categories