Node.js - Unit Testing Middleware - node.js

I have an api with a middleware function which I use to filter incoming requests. The functions checks the present of a token in the header, then makes two calls to the database, one to check the token and one to get some information and pass it on to the request object, if the 1st call was successful.
I am struggling to understand how to unit test this functions, by mocking up the request object and also the database calls.
middleware.js
exports.checkToken = function (req, res, next) {
if (!req.get('token')) {
return res.status(400).json('Bad request');
}
var token = req.get('token'); //get token from the header
User.findOne({'token': token}, function(err, user) {
// skipped error checking or no user found
Account.findOne({'_id': user.account}, function(err, account) {
// skipped error checking or no account found
req.somevalue = account;
return next();
});
});
};
Currently I am using mocha, chai and sinon and was thinking of the following:
mock User.findOne and Account.findOne using sinon.stub()
not really sure what to do about the req, res and next objects. How to emulate these?

I think the best choice is to use supertest.
https://www.npmjs.com/package/supertest
This package allow to run tests that emulate the full request cicle on the application.

Related

Express Session and Request Modules

In my website, everything server-side is stored in sessions of express-session.
But I can't understand why, when I make an HTTP request with request module, the req.session parameter isn't within the request.
I mean, follow the comments :
app.get('/prefix', async function(req, res) {
console.log(req.session.login);
// There ^ the req.session.login is true, and so it works
if (req.session.login == false || req.session.login == null) return;
var options = {
url: 'https://bot-dreamsub.glitch.me/getPermission',
json: true,
jar: true,
withCredentials: true
}
request(options, async function(err, res, json) {
if (err) {
throw err;
}
console.log(json);
if (json == true) {
res.sendFile(__dirname + '/prefix/prefix.html');
} else {
return;
}
});
});
app.get('/getPermission', function(req, res) {
console.log(req.session.login);
// There ^ the req.session.login is undefined, and so it sends null to the user
try {
if (req.session.login == false || req.session.login == undefined) {
res.send(null);
} else {
// some other code
}
} catch(err) {
console.log(err);
};
});
I don't know why request doesn't send sessions within the HTTP request even with
withCredentials: true
What can I do to accomplish it?
An express-session works by setting a cookie in the client's browser that made the original request. Future requests with that same cookie will offer access to that same session. When you do request() yourself from your server, you aren't presenting the same cookie that came in with the original /prefix request so you won't have access to the same session.
Since it appears you are just trying to use request() to call your own server, I'd suggest you just use a function call and pass the original req.session to that function call so that you will have it available.
You then use normal code factoring to factor out some common code between your /getPermissions route and what you want to use in your /prefix route so that they can both use and share a common function that you pass the current req and res to. Then you don't need to solve this cookie issue because you'll already have the right req object and thus the correct req.session in this factored common function.
Alternatively, you could build the right cookie and send that with your request() so that it will appear to be coming from the original browser that has the cookie (and thus session) that you want, but that's kind of the long way to do things when you already have the req.session you want and you could just pass it in a function call rather than start all over and try to simulate a cookie that will get you to the right session.
I don't know why request doesn't send sessions within the HTTP request even with
First off, session aren't sent with a request. Cookies are. Your server then uses the cookie to find the right session object.
Your call to request() does not have the right cookie in the cookie jar you use so when that requests gets to your server, it isn't able to find the right session object. So, when the request is received by your server, it appears to be coming from a different client that does not yet have a session so a new cookie and a new session are probably created for it.
FYI, if also looks like you may be confusing two definitions of res in your request() call. There's a res defined as an argument in this app.get('/prefix', async function(req, res) { and then you have a separate res in request(options, async function(err, res, json) { that will override the previous one in that scope. It appears to me when you do res.sendFile(__dirname + '/prefix/prefix.html');, you are probably using the wrong res. Probably the best way to solve this is to not use request() at all as suggested above (using a function call to your own server). But, if you were going to still use request(), then you need to name the two res arguments differently so you can still access them both and can use the correct one for your situation.

Unit testing promise based code in node.js express route/controller

Recently I switched from using callbacks to using promise in my rest api express app.
But I'm having trouble with unit testing routes/controller with async behaviour of the promise. Here is the sample code that needs to be unit tested.
var handler = function (req, res, next) {
var query = {},
var options = {
sort: { updatedAt: -1 },
limit: 10
};
if (req.query.before) {
query.updatedAt = { $lt: req.query.before };
}
// User.findAsync returns bluebird promise
User.findAsync(query, null, options).then(function (user) {
res.json(user);
}).catch(function (e) {
next(e);
});
}
router.get('/api/users', handler);
My approach to test above code was to spy on req, next, and User.findAsync and check if they are called with correct arguments. But because of async behaviour of the promise, I was having trouble to check if res.json or next are get called.
I've tried to stub findAsync to return resolved promise (Promise.resolve(user)). but still then callback is executed asynchronously.
I'm not sure if I'm on the right track for testing express application.
What is good strategy to test this kind of code in good separation?
I've also heard about using supertest.
But for me, Using supertest to test from http end point feels like more of integration testing which is not unit testing and is quite expensive.
Also, In general, I would like to know if it is good practice to try to cover all of the code with unit testing (models, controller, middleware, etc) and what's good strategies or techniques of doing that. Or If it is just good enough to test http end points with super test.
If your method being tested doesn't return a promise then you can't use the promise syntax in Mocha. You can test your method the same way you'd test any other asynchronous method - with done as a parameter of it. Let's say we want to test your handler function:
var handler = function (req, res, next) {
//...
User.findAsync(query, null, options).then(function (user) {
res.json(user);
}).catch(function (e) {
next(e);
});
}
We can write a test as such:
describe("The handler", function(){
it("calls res.json", function(done){ // note the done argument
handler({query: {before: 5}, // mock request
{json: done} // res.json calls our `done` param of this function
function(){ throw new Error("error called"); });
});
});
Note that we mocked the request, response and the next handler. Our mocked response has a json method that lets the test know it is complete (this can be a function if you want to make assertions inside it) and if next is called instead we throw to signal it's not something that was supposed to happen.

Are the req and res objects somehow accessible globally in NodeExpress?

req.session.username = user.username;
I'm using supertest to test route handling. In my log in handling code I have the following if a user logs in successfully -
req.session.username = user.username;
But in the supertest call back function I don't have access to the req object.
request(app)
.post('/login')
.send({username: 'dummy_username', password: 'valid_password'})
.end(function(err, res){
if (err) { return done(err); }
expect(err).toBe(null);
expect(res.status).toEqual(200);
done();
});
I would like to add in something like expect(req.session.username).toBe('dummy_username') but obviously I can't I do this when req is not available to me. So is there a way of referencing the req object?
Supertest is for testing the responses only, since testing the request (and the server-side manipulations thereof) would be testing implementation details instead of behavior. supertest isn't the right tool for this job. You can write pure unit tests for some of your server side functions, OR you can have the /login route include the user's information in the response body (which is typical) and have supertest verify that information matches what was in the request.

How to mock request and response in nodejs to test middleware/controllers?

My application has several layers: middleware, controllers, managers. Controllers interface is identical to middlewares one: (req, res, next).
So my question is: how can I test my controllers without starting the server and sending 'real' requests to localhost. What I want to do is to create request, response instances as nodejs does and then just call controllers method.
Something like this:
var req = new Request()
var res = new Response()
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)
Any advice is highly appreciated. Thanks!
P.S. the reason why I want to do this is that at the end I would like to test whether the response object contains correct variables for the jade views.
There's a semi decent implementation at node-mocks-http
Require it:
var mocks = require('node-mocks-http');
you can then compose req and response objects:
req = mocks.createRequest();
res = mocks.createResponse();
You can then test your controller directly:
var demoController = require('demoController');
demoController.login(req, res);
assert.equal(res.json, {})
caveat
There is at time of writing an issue in this implementation to do with the event emitter not being fired.
Since JavaScript is a dynamically typed language you can create mock objects and passing them to your controllers as follow:
var req = {};
var res = {};
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)
If your controller needs a particular piece of data or functionality from your request or response object you'll need to provide such data or functionality in your mocks. For example,
var req = {};
req.url = "http://google.com"; // fake the Url
var res = {};
res.write = function(chunk, encoding) {
// fake the write method
};
var next = function(err) {console.log('lala')}
controller.get_user(req, res, next)
I would try using dupertest for this. It's a node module I created for the very purpose of easy controller testing without having to spin up a new server.
It keeps the familiar syntax of node modules like request or supertest, but again, without the need to spin up a server.
It runs a lot like Hector suggested above, but integrates with a test framework like Jasmine to feel a little more seamless.
An example relating to your question may look like:
request(controller.get_user)
.params({id: user_id})
.expect(user, done);
Or the more explicit longhand version:
request(controller.get_user)
.params({id: user_id})
.end(function(response) {
expect(response).toEqual(user);
done();
});
Note: the examples assume user_id and user are defined somewhere, and that the controller grabs and returns a user based on id.
Edit: reading your response to an answer above, I will admit the downside currently is that this module does not integrate a more robust mock request or response object by default. dupertest makes it super easy to extend and add properties to both req and res, but by default they are pretty bare.
If you want to use the real req and res objects, you have to send real requests to the server. However this is much easier than you might think. There are a lot of examples at the express github repo. The following shows the tests for req.route
var express = require('../')
, request = require('./support/http');
describe('req', function(){
describe('.route', function(){
it('should be the executed Route', function(done){
var app = express();
app.get('/user/:id/edit', function(req, res){
// test your controllers with req,res here (like below)
req.route.method.should.equal('get');
req.route.path.should.equal('/user/:id/edit');
res.end();
});
request(app)
.get('/user/12/edit')
.expect(200, done);
})
})
})
A bit old post, but I would like to give my 2 cents. The approach you want to take depends on whether you are doing unit testing or integration testing. If you are going down the route of using supertest, that means you are running the actual implementation code and that means you are doing integration testing. If that's what you want to do this approach is fine.
But if you are doing unit testing, you would mock req and res objects (and any other dependencies involved). In the below code (non-relevant code removed for brevity), I am mocking res and giving just a mock implementation of json method, as that's the only method I need for my tests.
// SUT
kids.index = function (req, res) {
if (!req.user || !req.user._id) {
res.json({
err: "Invalid request."
});
} else {
// non-relevent code
}
};
// Unit test
var req, res, err, sentData;
describe('index', function () {
beforeEach(function () {
res = {
json: function (resp) {
err = resp.err;
sentData = resp.kids;
}
};
});
it("should return error if no user passed in request", function () {
req = {};
kidsController.index(req, res);
expect(err).to.equal("Invalid request.");
});
/// More tests....
})
Take a look at node-tdd and the useNock flag. It builds on top of mocha and nock and automatically creates and uses a recording file for each test.
We love that it's so easy to use. Basically just "enable and forget" and focus on writing requests / test cases. If requests for a test change, one still needs to delete or adjust the recording file, but at least it's entirely separate from the code.

Express/Node.JS middleware raising error, keeps processing

I'm beginning under NodeJS/Express and I'm facing the following problem (I probably didn't get all the tricks of async programming yet)
I've made a middleware in charge of checking if a oauth_token paramters is passed (actually implementing oAuth layer on my node server)
I'm doing this :
function myMiddle(req,res,next) {
var oAuthToken = req.query["oauth_token"];
if (oAuthToken == undefined) {
res.send(406);
res.end();
next(new Error('No token provided'));
}
/* Basically doing some DB stuff with MongoDB, connecting and using oAuthToken provided to query, etc.. */
The thing is that I expected the code to "die" when he doesn't receive the oauth_token parameters in the query string. It's actually raising me an error and returning greatly 406 error to my HTTP client, but code keeps processing behind and raises me mutable headers errors caused by my processing code after, and my script dies.
Something I'm missing? Thanks by advance.
If your oAuthToken is undefined Node.js makes a response. After that you fire next(...) which tries to make another response to the same request. This fails and you see what you see. Note that in Node.js using res.send(); and res.end(); does not stop your function. So what you need is to do the following:
function myMiddle(req,res,next) {
var oAuthToken = req.query["oauth_token"];
if (oAuthToken == undefined) {
next(new Error('No token provided')); // handle everything here
// res.send(406);
// res.end();
// unnecessary, throws errors because you try to respond twice
}
// do something if this is fine
}
or do it the other way - use res.send(406); res.end(); without next(...).
This might be late, but I just encountered this issue as well. You can actually pass the error to ErrorHandler so that the middleware would not continue to the next middleware or router while you can send the HTTP status code you want.
Your middleware
function myMiddle(req, res, next) {
// Do validate your OAuth token
// you might want to do better validation of the token here
// instead of just checking its existence
//
// var oAuthToken = req.query['oauth_token'];
//
// According to JSLint, you can just directly select the object as:
//
// req.query.oauth_token
if (req.query.oauth_token === undefined) {
// Just let the ErrorHandler does the rest
// like redirecting or just send message to client
var err = new Error('Unauthorized access.');
err.status(406); // Or 403, or any HTTP status code
// Pass it to ErrorHandler
next(err);
} else {
// Do something here, or just
next();
}
}
Your ErrorHandler
app.use(function(err, req, res, next){
if (err.status == 406) {
// You can just do res.sendStatus()
res.sendStatus(406); // Set HTTP status code as 406 and send message to client
// Or chaining res.status() with res.send()
res.status(406).res.send(); // or res.render(), or res.json()
return;
}
// Others
});
More about ErrorHandler: http://expressjs.com/ja/guide/error-handling.html
Do you have express error handling (app.use(express.errorHandler()) in your middleware stack?
Also see the Express middleware section for details on how to use next().

Resources