Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different NODE_ENV for different tests

I'm testing my API with supertest

I want to check that my CSRF token protection works, and then disable it for the other tests.

For this I set NODE_ENV to test or not_test

app.js

var csrf = require('csurf');   
var app  = express();
if (process.env.NODE_ENV !== 'test') {
  app.use(csrf({ cookie: true }));
  app.use(function(req, res, next) {
    res.cookie('XSRF-TOKEN', req.csrfToken());
    return next();
  });
}

Test CSRF

process.env.NODE_ENV = 'not_test';

var app = require("app.js");
var request = require('supertest')(app);

var testAccount = {
  "login": "test",
  "pass": "test"
};

describe('CSRF protection', function() {
  it('On /login', function(done){
    request
      .post('/login')
      .send(testAccount)
      .expect(403, done);
  });
});

Test login NODE_ENV is now test

process.env.NODE_ENV = 'test';

var app = require("app.js");
var request = require('supertest').agent(app);

var testAccount = {
  "login": "test",
  "pass": "test"
};

describe('API Admin roads', function() {
  before(function (done) {
    request
      .post('/login')
      .send(testAccount)
      .end(done);
  });

  it('/api/admin/groups/', function(done){
    request
      .get('/api/admin/groups/')
      .expect(200, done);
  });
});

The problem is, only the first process.env.NODE_ENV is taken into account, if I set it to not_test and then to test I will still be in not_test mode.

like image 912
IggY Avatar asked Jan 04 '16 16:01

IggY


1 Answers

Tests can be executed in many ways and their execution sequence isn't always the same (e.g. mocha --parallel option).

So, rewriting a shared variable like process.env and manipulating the require cache it's not a reliable way to solve this problem. It's an hack - today it works but it will broke easily when you (or your workmates) will change the code tomorrow.

An easier and cleaner way to do that, is to return an init function from your app.js file and get the app object as the output of this function call. In this way you can pass to it an explicit option to enable/disable the csrf token protection:

//app.js
module.exports = function appSetup(opts) {
  var csrf = require('csurf');   
  var app  = express();

  if (opts.csrfEnabled) {
    app.use(csrf({ cookie: true }));
    app.use(function(req, res, next) {
      res.cookie('XSRF-TOKEN', req.csrfToken());
      return next();
    });
  }

  // more express handlers ...
  // app.use(...)

  return app;
}

Then disable it in your tests without csrf:

var app = require("app.js");
var request = require('supertest')(app({csrfEnabled: false}));

And enable it where you need it:

var app = require("app.js");
var request = require('supertest')(app({csrfEnabled: true}));

Obviously you need to update you app start code, e.g. bin/start.js:

var app = require('../app')({csrfEnabled: true});
var server = http.createServer(app);
// and so on...

Advantages of this approach:

  • no specific execution order required
  • no concurrency problems
  • explicit setup of your app
like image 72
lifeisfoo Avatar answered Sep 21 '22 17:09

lifeisfoo