Search code examples
node.jsexpressmocha.jssupertest

Mocha- Testing Express API With Global Middleware


This is my first express app and I'm having some trouble writing the tests for the endpoints.

At the moment I am not using a database to store the tokens or check for users when logging in.

The tests I have written pass, however they are not complete. How would I test for example a user that has authenticated correctly?

How would I mimmic the authentication process?

Because I've created a global middleware function and checkLoging function how to factor these into my tests?

How do I test the authentication and routs?

This is my code:

'use strict';

var express = require('express');
var session = require('express-session');
var app = express();
var shopifyObj = require('shopify-node');
var shopifyConfig = require("./config/config.json");
var _ = require('underscore');
var shopify = require('./lib/modules/shopify');
var product = require('./lib/modules/product');

var fakeDta = {
    user: 'Simba',
};

app.use(session({
  secret: 'dsjkfhklaf',
  resave: false,
  saveUninitialized: true
}));

// Set global middleware function to check for sessions
app.use(function(req, res, next) {
    if (req.session && req.session.user) {
        // check here for the existence of this user and token in the database.
        next();

    } else {
        next();
    }
});

// define middleware function
function checkLogin (req, res, next) {
    if (!req.session.user) {
        req.session.error = 'Access denied!';
        res.redirect('/auth');
    } else {
        next();
    }
};

app.get('/auth', function (req, res) {
    var url = shopify.createURL();
    res.redirect(url);
});

app.get('/handle-o-auth-response', function (req, res) {
    var code = req.query.code;
    _.extend(shopifyConfig, {'code': code});
    shopify.setAccessCode(code, res, function (err, token) {
        if (err) {
            res.redirect('/auth');
        } else {
            _.extend(shopifyConfig, {'access_token': token});
            req.session.user = fakeDta.user;
            res.redirect('/products');
        }
    });
});

app.get('/products', checkLogin, function (req, res) {

    shopify = new shopifyObj(shopifyConfig);

    product.getProducts(shopify, res);

});

var server = app.listen(3000, function () {
  var host = server.address().address;
  var port = server.address().port;

  console.log('App listening at http://%s:%s', host, port);
});

module.exports = server;

These are my tests:

"use strict";

var supertest = require("supertest");

describe("Testing server",function(){
    var server;

    beforeEach(function () {
        /* Node module system caches the evaluated result of each module
        to avoid loading and compiling the same javascript file multiple times.
        Thus the server instance from app.js is only created once.
         */
        delete require.cache[require.resolve('../app.js')];
        server = require('../app.js');
    });

    afterEach(function (done) {
        server.close(done);
    });

    it("should redirect to /auth",function(done){
        supertest(server)
            .get("/products")
            //.expect("Content-type",/json/)
            .expect(302, done);
    });

    it("should redirect to shopify",function(done) {
        supertest(server)
            .get("/auth")
            //.expect("Content-type",/json/)
            .expect(302, done);
    });
});

Solution

  • What I would do is refactor the code to support mocking or replacing the checkLogin function.

    You can do this by exposing the checkLogin function on the exported object from app.js, but that wouldn't be very clean architecturally.

    Alternatively you can separate the checkLogin function out to it's own file, and instead of exporting:

    module.exports = server;
    

    create a function that takes the checkLogin function that wraps all code that uses the checkLogin function:

    var express = require('express');
    // skipped require code...
    var product = require('./lib/modules/product');
    
    function init(checkLogin) {
    
        var fakeDta = {
            user: 'Simba',
        };
    
        // skipped code...
    
        // remove the checkLogin function definition!
    
        // skipped code...
    
        return app.listen(3000, function () {
            var host = server.address().address;
            var port = server.address().port;
    
            console.log('App listening at http://%s:%s', host, port);
        });
    };
    
    module.exports = init;
    

    Now in your live code you require the file with the checkLogin function, and pass the function into server, and in your testing code you can supply a mock function that will behave exactly as you want in your test, for example returning that the login is valid.

    You can either write your own function and pass that one, or use a mocking library such as sinon.