7

I have just started using supertest and nock to write unit tests for middleware in my express application.

All requests handled by my router setup are first checked for the presence of a session property (I am using express-session).

app.use('/api', helpers.hasAccessToken, require('./routes.js'));

The helper simply does:

module.exports.hasAccessToken = function(req, res, next) {
  if(req.session.accessToken){
    next();
  } else {
    res.status(401).send('LOGIN_SESSION_ENDED');
  }
};

In my test spec I have:

var app = require('./index.js'),
  request = require('supertest')(app),
  expect = require('chai').expect,
  nock = require('nock');


    describe('GET requests', function(){
      beforeEach(function(){
        nock('https://somedomain:port')
          .get('/someendpoint')
          .reply(200, {foo:'bar'});
      });
      it('should return a 200 HTTP status code', function(done){
        request
          .get('/api/someendpoint')
          .end(function(err, res){
            expect(res.status).to.equal(200);
            done();
          });
      });
    });

This errors with a status of 401 being returned instead, which I know is down to the req.session.accessToken property not having been set prior to the tests being run.

So how can I get at the req object to do what I like with the session object?

Thanks

mindparse
  • 6,115
  • 27
  • 90
  • 191

1 Answers1

1

The workflow is: user sign in => set req.session.accessToken in server-side => response to client-side with set-cookie response header(cookie set by express-session) => call protected /api/someendpoint endpoint with accessToken cookie. => call hasAccessToken middleware => rest code logic.

Here is the solution:

app.js:

const express = require("express");
const session = require("express-session");
const helpers = require("./helpers");
const app = express();

app.set("trust proxy", 1);
app.use(
  session({
    secret: "keyboard cat",
    resave: false,
    saveUninitialized: true,
  }),
);

app.post("/signin", (req, res) => {
  req.session.accessToken = "123123";
  console.info("signin success");
  res.status(200).end();
});

app.use("/api", helpers.hasAccessToken, require("./routes"));

const server = app.listen(3000, () => {
  console.info(`HTTP server is listening on http://localhost:${server.address().port}`);
});

module.exports = server;

helpers.js:

module.exports.hasAccessToken = function(req, res, next) {
  console.log("req.session.accessToken", req.session.accessToken);
  if (req.session.accessToken) {
    next();
  } else {
    res.status(401).send("LOGIN_SESSION_ENDED");
  }
};

routes.js:

const { Router } = require("express");

const router = Router();

router.get("/someendpoint", (req, res) => {
  res.sendStatus(200);
});

module.exports = router;

app.test.js:

const app = require("./app");
const request = require("supertest");
const expect = require("chai").expect;

describe("GET requests", function() {
  const agent = request(app);
  let cookies;

  before((done) => {
    agent.post("/signin").expect(200, (err, res) => {
      if (err) return done(err);
      expect(res.headers).to.have.property("set-cookie");
      cookies = res.headers["set-cookie"].pop().split(";")[0];
      done();
    });
  });
  after((done) => {
    app.close(done);
  });

  it("should return a 200 HTTP status code", function(done) {
    console.log(cookies);
    agent
      .get("/api/someendpoint")
      .set("Cookie", [cookies])
      .end(function(err, res) {
        if (err) return done(err);
        expect(res.status).to.equal(200);
        done();
      });
  });
});

Integration test result with coverage report:

HTTP server is listening on http://localhost:3000
  GET requests
signin success
connect.sid=s%3Ahw94RpxRocC4hMTkCmMx4Ot85aGYG6s5.eE91ELDNjuQ1fWqEsRZdwtwKokLXR6%2Bao9NGdvl%2Bflc
req.session.accessToken 123123
    ✓ should return a 200 HTTP status code


  1 passing (33ms)

-------------|----------|----------|----------|----------|-------------------|
File         |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-------------|----------|----------|----------|----------|-------------------|
All files    |    93.33 |       50 |      100 |    97.67 |                   |
 app.js      |      100 |      100 |      100 |      100 |                   |
 app.test.js |    90.48 |       50 |      100 |      100 |             11,27 |
 helpers.js  |       80 |       50 |      100 |       80 |                 6 |
 routes.js   |      100 |      100 |      100 |      100 |                   |
-------------|----------|----------|----------|----------|-------------------|

Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/39118250

Lin Du
  • 88,126
  • 95
  • 281
  • 483