Testing expressjs
authenticated routes
Testing authenticated routes sound intimidating, but the trick is simple to get it right. The right combination of mocking a session object and stubbing of the authentication middleware. This article will revisit these two key ingredients to make tests work.
In this article we will talk about:
- Avoiding integration test trap on authenticated routes
- Stubbing authentication middleware for faster tests
- Mocking authentication protected routes' session data
Even though this blog post was designed to offer complementary materials to those who bought my Testing
nodejs
Applications book, the content can help any software developer to tuneup working environment. You use this link to buy the book.
Show me the code
// Authentication Middleware in middlwares/authenticated.js
module.exports = function(req, res, next){
let token = req.headers.authorization;
let payload = jwt.decode(token, config.ssecret);
if(!validate(payload)) return next(new Error('session expired'));
req.user = payload.sub;//adding
return next();
};
//Session Object in settings/controller/get-profile
module.exports = function(req, res, next){
let user = req.session.user;
UserModel.findById(user._id, (error, user) => {
if(error) return next(error, null);
return req.status(200).json(user);
});
};
//Router that Authentication Middleware
var router = require('express').Router();
var authenticated = require('./middleware/authenticated');
var getProfile = require('./settings/get-profile');
router.get('/profile/:id', authenticated, getProfile);
module.exports = router;
What can possibly go wrong?
There is a clear need to mimic the real authentication when testing expressjs
authenticated routes and sometimes this need leads to an integration testing trap.
Following are other challenges we may expect along the way:
- Avoid testing underlying libraries that provide authentication features
- Simulate authenticated session data
- Mock requests behind protected third-party routes, such as Payment Gateways, etc.
Choosing tools
If you haven't already, reading “How to choose the right tools” blog post gives insights on a framework we used to choose the tools we suggest in this blog.
Following our own “Choosing the right tools” framework. They are not a suggestion, rather the ones that made sense to complete this article:
- We can choose amongst a myriad of test runners, for instance,
jasmine
(jasmine-node
),ava
orjest
.mocha
was appealing in the context of this writeup, but choosing any other test runner does not make this article obsolete. supertest
framework for mocking RESTful APIs andnock
for intercepting and mocking third-party HTTP requests.supertest
is written on top ofsuperagent
, so we get both testing toolkits.- Code under test is instrumented, but default reporting tools do not always suits our every project's needs. For test coverage reporting we recommend
istanbul
.
Workflow
It is possible to generate reports as tests progress.
latest versions of
istanbul
usesnyc
name.
# In package.json at "test" - add next line
> "istanbul test mocha -- --color --reporter mocha-lcov-reporter specs"
# Then run the tests using
$ npm test --coverage
Show me the tests
If you haven't already, read the “How to write test cases developers will love”
The keyword in mocking a session object lies in this line, found in the example above: let user = req.session.user;
. With that knowledge,
describe('getPrifile', () => {
let req, res, next, error;
beforeEach(() => {
next = sinon.spy();
sessionObject = { user: { /*...*/ } };//mocking session object
req = { params: {id: 1234}, session: sessionObject };
res = { status: (code) => { json: sinon.spy() }}
});
it('returns a profile', () => {
getRequest(req, res, next);
expect(res.status().json).toHaveBeenCalled();
});
});
On the other hand, since authenticated()
resides on a library, it can simply be stubbed as any other function, the time comes to test the whole route: let authenticated = sinon.spy();
.
Conclusion
In this article, we reviewed how testing tends to be more of an art, than science. We also stressed the fact that, like in any art, practice makes perfect.
One use case of tapping into middleware re-usability/composability and testability is the authentication middleware herein presented. Writing a good meaningful message is pure art. There are additional complimentary materials in the “Testing nodejs
applications” book.
References
- Testing
nodejs
Applications book - Local Authentication with Passport and Express ~ Michael Herman Blog
- How to test with Auth0 protected route ~ StackOverflow Question
- How to modularize your Chat Application