Search code examples
javascriptnode.jsmocha.jspassport.jssupertest

Mocha Testing authenticated APIs provided by a server


My APIs can be used after logging in. For example, /api/sales requires an authentication like below code. (I'm using Node.js and Express.js)

app.all('/api/*', userController.requiresLogin);
app.route('/api/sales')
    .get(common.readAll(Sale, 'name'))
    .post(common.create(Sale));

The userController.requiresLogin function is:

exports.requiresLogin = function(req, res, next) {
    console.log('Check login...');

    if(!req.user) return res.status(401).send();
    else next();
};

To login, I use passport.js.

exports.login = passport.authenticate('local', {
    successRedirect: '/',
    failureRedirect: '/login',
    failureFlash: false
});

When I run my server and test this API on my browser, it works.

To test this API automatically, I built my Mocha test code using Supertest.

    var request = require('supertest'),
        should = require('should'),
        utils = require('./../utils'),
        mongoose = require('mongoose');

    var Sale = mongoose.model('Sale'),
        User = mongoose.model('User');

    var user;

    describe('Controller Testing:', function() {
        describe('Sale Controller Testing:', function() {
            var agent = request.agent();

            beforeEach(function(done) {
                user = new User({
                    username:'defaultUser@test.com',
                    password: 1234,
                    userId: 'default user'
                });
                user.save();

                request(app).post('/login')
                    .send( {username: 'defaultUser@test.com', password: '1234'} )
                    .end(function(err, res) {
                        should.not.exist(err);
                        agent.saveCookies(res);
                        done();
                    });
            });

            afterEach(function() {
                User.remove({}, function(){});
                Sale.remove({}, function(){});
            }

            it('create a sale', function(done) {
                var sale = {
                    name: 'New Sale',
                    orders: [order1]
                };

                var req = request(app).post('/api/sales')
                    .send(sale)
                    .expect('Content-Type', /json/)
                    .expect(200);
                agent.attachCookies(req);

                req.end(function(err, res) {
                    console.log(res.statusCode);

                    res.body.name.should.equal('New Sale');
                    done();
                });
            });
    }

This test keeps failed. The test 'create a sale' cannot identify that a user has logged in, even though I saved and attached corresponding cookies.


Solution

  • Check supertest npm page, and revise your test script as

       var request = require('supertest'),
        should = require('should'),
        utils = require('./../utils'),
        mongoose = require('mongoose');
    
    var Sale = mongoose.model('Sale'),
        User = mongoose.model('User');
    
    var user;
    
    describe('Controller Testing:', function() {
        describe('Sale Controller Testing:', function() {
            var agent = request.agent(app); // revised
    
            beforeEach(function(done) {
                user = new User({
                    username:'defaultUser@test.com',
                    password: 1234,
                    userId: 'default user'
                });
                user.save();
    
                agent.post('/login') // revised
                    .send( {username: 'defaultUser@test.com', password: '1234'} )
                    .expect("set-cookie", "check correct cookie here") // appended
                    .end(function(err, res) {
                        should.not.exist(err);
                        //agent.saveCookies(res);
                        // or write some code to check your cookie here
                        done();
                    });
            });
    
            afterEach(function() {
                User.remove({}, function(){});
                Sale.remove({}, function(){});
            }
    
            it('create a sale', function(done) {
                var sale = {
                    name: 'New Sale',
                    orders: [order1]
                };
    
                var req = agent.post('/api/sales') // revised
                    .send(sale)
                    .expect('Content-Type', /json/)
                    .expect(200);
                //agent.attachCookies(req);
    
                req.end(function(err, res) {
                    console.log(res.statusCode);
    
                    res.body.name.should.equal('New Sale');
                    done();
                });
            });
    }
    

    I didn't see where is app come from in your test script, assume you got it correctly and applied cookieParser middleware.

    I think the problem in your test script is request just sent before cookie attached. Maybe you can also try

    var req = aggent.attachCookies(request(app));
    req.post('/api/sales')
         .send(sale)
         .expect('Content-Type', /json/)
         .expect(200);
         //agent.attachCookies(req);