Different NODE_ENV for different tests
Asked Answered
M

1

6

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.

Mchale answered 4/1, 2016 at 16:59 Comment(6)
Hey Why don't you change the process.env.NODE_ENV in the before function and the after function?Tarnetgaronne
@Tarnetgaronne Because process.env.NODE_ENV need to be set before app.js is included as the test NODE_ENV !== 'test' is done in app.jsMchale
i have an idea but never tried it my self.Not sure if it's good one. maybe trying to require inside the describe scope ( I know it's not recommended in real web server but maybe it will work and would be just good enough to this test) -it might wrote a shot?Tarnetgaronne
same problem, adding console.log("ENV : " + process.env.NODE_ENV); in app.js only display one line (although there is 2 tests from 2 differents files run). I guess the require('app.js'); is kept in cacheMchale
delete require.cache[require.resolve("app.js")] makes it work !!!Mchale
just saw that in online search :)Tarnetgaronne
S
1

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
Subspecies answered 23/3, 2021 at 13:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.