dbarton91
dbarton91

Reputation: 101

Sinon stub is being ignored outside of the test file

I'm using mocha, chai, and sinon to test some authenticated API routes. I'm using passport.authenticate() as middleware to authenticate the route:

const router = require('express').Router();
const passport = require('passport');

router.post('/route', 
            passport.authenticate('jwt', {session:false}), 
            function(req,res) {
                return res.status(200);
            });
module.exports = router;

Then, in my test suite, I am using sinon to stub out passport.authenticate calls:

const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
const passport = require('passport');
const server = require('../../app');
const expect = chai.expect;

chai.use(chaiHttp);
describe('route', function() {
    before(function(done) {
        sinon.stub(passport, 'authenticate').callsFake(function(test, args) {
            console.log('Auth stub');
        });
        console.log('stub registered');
        passport.authenticate('jwt', {session:false});
        done();
    });
    after(function(done) {
        console.log('after hook');
        passport.authenticate.restore();
        done();
    });
    describe('POST /route', function() {
        it('should post', function(done) {
            console.log('starting test');
            chai.request(server)
                .post('/route')
                .end(function(err,res) {
                    expect(res).to.have.status(200);
                    done();
                });
         });
     });
});

Now, when I run the test suite, I see it print out the following:

  route
stub registered
Auth stub
    POST /route
starting test
      1) should post
after hook

1 failing

1) route
   POST /route
     should post:

   Uncaught AssertionError: expected { Object (_events, _eventsCount, ...) } to have status code 200 but got 401

From this, we can see that the after the stub is registered, I can call it in the test file and it is properly stubbed. But when passport.authenticate() is called in route.post(), it is the actual passport.authenticate() and sends a response with status 401 because I'm not authenticated.

Any thoughts on what's going on?

Upvotes: 2

Views: 763

Answers (2)

mariano_c
mariano_c

Reputation: 421

One small note related to sinon stubbing, I manage to have it working using this syntax:

passport.authenticate = sinon.stub( passport, 'authenticate' )
  .returns(
    ( req, res, next ) => {

      const user = {
        username: 'test user',
        email: '[email protected]',
        displayName: 'Test User',
        provider: 'testCase',
        roles: [ 'admin' ]
      }

      req.user = user;

      next()
    }
  );

Main things are:

  1. add user data into req.user
  2. execute next() for code flow to continue into next middleware

To recap this is not a way to actually test your authentication strategy but to bypass it in case you want to test router results.

Upvotes: 1

Brian Adams
Brian Adams

Reputation: 45780

Your code calls passport.authenticate as soon as it runs, and it runs as soon as it is required.

Because you are requiring the code at the beginning of the test before the stub has been created your code ends up calling the real passport.authenticate.

In order for code like this to call the stub you must set up your stub before you require your code:

const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
const passport = require('passport');
// const server = require('../../app');  <= don't require your code here...
const expect = chai.expect;

chai.use(chaiHttp);
describe('route', function () {
  before(function (done) {
    sinon.stub(passport, 'authenticate').callsFake(function (test, args) {
      console.log('Auth stub');
    });
    console.log('stub registered');
    passport.authenticate('jwt', { session: false });
    done();
  });
  after(function (done) {
    console.log('after hook');
    passport.authenticate.restore();
    done();
  });
  describe('POST /route', function () {
    it('should post', function (done) {
      console.log('starting test');
      const server = require('../../app');  // <= require it AFTER creating the stub
      chai.request(server)
        .post('/route')
        .end(function (err, res) {
          expect(res).to.have.status(200);
          done();
        });
    });
  });
});

Upvotes: 1

Related Questions