Testing Authenticated Requests with Supertest

Like many others, my project also has a REST API that requires authentication. The API is built on Node.js using Express 3 and Passportto handle authentication sessions, which means that a session identifier is stored in a cookie and attached to all requests. Supertest is used for testing.

Many approaches to testing authenticated requests involve using another library, like superagent, for handling the authentication. In the following will summarize two alternative with superagent and manual handling of login cookies.

1. Create an authenticated request object

In the following we will create an authenticated request object with superagent and use it to make subsequent requests. Please find the original Stackoverflow article here.

var request = require('superagent');
// Auxiliary function.
function createAuthenticatedRequest(server, loginDetails, done) {
var authenticatedRequest = request.agent();
authenticatedRequest
.post(server)
.send(loginDetails)
.end(function(error, response) {
if (error) {
throw error;
}
done(authenticatedRequest)
});
// Using auxiliary function in test cases.
createAuthenticatedRequest(server, loginDetails, function(request) {
request
.get('/admin')
.expect(200, done);
});

Also note, that supertest uses superagent internally. Thus you can access superagent from supertest as follows.

var request = require('supertest').agent();

2. Manage cookies manually

In the following we will use superagent to save cookies from the login request and insert them into subsequent requests manually. Please find the original JakeTrent.com article here.

var request = require('supertest');
// Auxiliary function.
function createLoginAgent(server, loginDetails, done) {
request
.post(server)
.send(loginDetails)
.end(function (error, response) {
if (error) {
throw error;
}
var loginAgent = request.agent();
loginAgent.saveCookies(res);
done(loginAgent);
});
};
// Using auxiliary function in test cases.
createLoginAgent(server, loginDetails, function(agent) {
var request = request.get('/admin');
agent.attachCookies(request);
request.expect(200, done);
});

Using Express applications with superagent

There are two ways to starting the server application when using supertest. It can be started manually and its URL passed to supertest. Alternatively the Express application can be passed to supertest, in which case supertest automatically starts the server application. Examples of these approaches are shown below.

var request = require('supertest');
// Start server manually and use an URL.
request
.get('http://127.0.0.1:3000/myapi/admin')
.expect(200, done);
// Pass Express application to supertest and start automatically.
var myserver = require('../../myserver') // Express application
request(myserver)
.get('/myapi/admin')
.expect(200, done);

When testing with superagent, the Express application has to be passed to the agent() call for the latter approach to work. Thus our previous auxiliary function for creating an authenticated request would look like the following. Changed lines are highlighted in bold.

var request = require('superagent');
var myserver = require('../../myserver') // Express application
function createAuthenticatedRequest(loginDetails, done) {
var authenticatedRequest = request.agent(myserver);
authenticatedRequest
.post('/login')
.send(loginDetails)
.end(function(error, response) {
if (error) {
throw error;
}
done(authenticatedRequest)
});

Handling cookies manually

Handling the login cookie manually is an alternative to using superagent. The cookie needs to be extracted from the response to the login call and inserted into subsequent requests. An example of this approach is shown below.

// Auxiliary function.
function createLoginCookie(server, loginDetails, done) {
request()
.post(server)
.send(loginDetails)
.end(function(error, response) {
if (error) {
throw error;
}
var loginCookie = response.headers[‘set-cookie’];
done(loginCookie);
});
}
// Using auxiliary function in test cases.
createLoginCookie(server, loginDetails, function(cookie) {
request(application)
.get('/myapi/admin')
.set('cookie', cookie)
.expect(200, done);
});

I choose to handle the login cookie manually in my test cases. In my opinion, it makes it clear that cookies are being included in the requests for authentication purpose. superagent hides that fact, but does save that one line for setting the cookie.