I have an express app with API endpoints secured using JWT
token. I have a method that verifies a received token.
// authentication.js
import jwt from 'jsonwebtoken';
import Settings from '../settings';
const AuthenticationMiddleware = {
verifyToken: (req, res, next) => {
const token = req.headers['x-access-token'];
if (!token) {
const msg = 'Include a valid token in the x-access-token header';
return res.status(422).json({
error: 'No token provided',
msg
});
}
try {
req.user = jwt.verify(token, Settings.jwtSecret);
req.token = token;
return next();
}
catch (e) {
return res.status(422).json({ error: 'Invalid token' });
}
}
};
export default AuthenticationMiddleware;
This works fine when I call the API endpoints from postman
with the token header included.
Now I have a test such as shown below. There's about 40 of them, each requiring a token to be sent with each API request.
// should is not used directly in the file but is added as a mocha requirement
import supertest from 'supertest';
import app from '../app';
const server = supertest.agent(app);
const BASE_URL = '/api/v1';
describe('/loans: Get all loans', () => {
it('should return a list of all loans', done => {
server
.get(`${BASE_URL}/loans`)
.expect(200)
.end((err, res) => {
res.status.should.equal(200);
res.body.data.should.be.an.instanceOf(Array);
for (const each of res.body.data) {
each.should.have.property('id');
each.should.have.property('userid');
}
done();
});
});
});
I've looked at sinon
and tried stubbing the verifyToken
function in mocha's before
hook like so
import sinon from 'sinon';
import AuthenticationMiddleware from '../middleware/authentication';
before(() => {
const stub = sinon.stub(AuthenticationMiddleware, 'verifyToken');
stub.returnsThis()
});
But I can already see a problem here. While the verifyToken
stub may have been created, it is NOT used during the test. The verifyToken
that is being called during the test is passed as middleware from the route like so
router.get('/loans', AuthenticationMiddleware.verifyToken, LoansController.get_all_loans);
I want a way to stub verifyToken
during the test so that I can just return next()
immediately.
My question is, is it possible to stub AuthenticationMiddleware.verifyToken
universally during the test so that all calls to the API endpoint call the stubbed version?
According to these two posts, Sinon stub being skipped as node express middleware and How to mock middleware in Express to skip authentication for unit test?, the reason for my stub not being active was that the app
was being imported and cached before the stub is even created, so the app uses the one it has cached.
So the solution was to alter the required function before the app gets a chance to cache it. What I did was that (I stumble upon it by trial and error) was to create a file in my test folder called stubs.js
and here's the content.
import sinon from 'sinon';
import AuthenticationMiddleware from '../middleware/authentication';
sinon.stub(AuthenticationMiddleware, 'verifyToken').callsFake(
(req, res, next) => next()
);
Then I require this file in my test runner in package.json
like so
"scripts": {
"test": "nyc --reporter=html --reporter=text --reporter=lcov mocha -r @babel/register -r should -r test/stubs.js"
},