问题
I'm trying to follow this example: https://www.alexjamesbrown.com/blog/development/stubbing-middleware-testing-express-supertest/ but the sinon stub doesn't seem to be executing the wrapped code. I've seen lots of stackoverflow posts regarding this issue but none of the answers have helped me figure out what I'm doing wrong. Whenever I run my test I get the following error:
1) should return a list of sites
0 passing (42ms) 1 failing
- GET /api/config/buildPro/sites should return a list of sites: Error: expected 200 "OK", got 403 "Forbidden" at Test._assertStatus (node_modules\supertest\lib\test.js:268:12) at Test._assertFunction (node_modules\supertest\lib\test.js:283:11) at Test.assert (node_modules\supertest\lib\test.js:173:18) at Server.localAssert (node_modules\supertest\lib\test.js:131:12) at emitCloseNT (net.js:1655:8) at processTicksAndRejections (internal/process/task_queues.js:83:21)
This leads me to believe it's not calling the stub code but instead executes the actual authorization function. Here's my code:
app.js
const express = require('express');
const app = express();
const authorization = require('./security/authorization');
const configRoutes = require('./api/routes/config');
app.all('/api/*', authorization.authorize);
app.use('/api/config', configRoutes);
module.exports = app;
authorization.js
const aad = require('azure-ad-jwt');
module.exports.authorize = (req, res, next) => {
if(!req.headers.authorization){
res.status(403).json({
message: "Auth failed"
});
return;
}
const jwtToken = req.headers.authorization.replace('Bearer ', '');
aad.verify(jwtToken, null, function (err, result) {
if (result) {
next();
} else {
res.status(401).json({
message: "Auth failed"
});
}
});
};
config.spec.js
const request = require('supertest');
const sinon = require('sinon');
const app = require('../app');
const authorization = require('../security/authorization');
var agent;
describe('GET /api/names', () => {
before(() => {
ensureAuthenticatedSpy = sinon.stub(authorization, 'authorize');
ensureAuthenticatedSpy.callsArgWithAsync(2);
agent = require('supertest')
.agent(require('../app'));
});
it('should return a list of names', done => {
agent
.get('/api/config/buildPro/sites')
.expect(200)
.end((err, res) => {
if (err) return done(err);
done();
});
});
});
回答1:
instead executes the actual authorization
This is exactly what is happening.
Note this code in server:
app.all('/api/*', authorization.authorize);
This resolves authorize
function reference in this particular state of program and express will use this particular function (original one!) for rest of program.
This:
ensureAuthenticatedSpy = sinon.stub(authorization, 'authorize');
is called later and given that sinon has no power to change references to original authorize
captured previously ... is no-op.
IOW, dependency injection in basic Javascript apps is not as simple as one might want.
To workaround, you can change original route in app.js
:
app.all('/api/*', (req, res, next) => authorization.authorize(req, res, next));
Now, your closure would resolve authorization.authorize
every-time it's called, enabling mocking/spying function you're interested in. However this is solution is far from elegant.
来源:https://stackoverflow.com/questions/62626143/why-is-my-sinon-stub-acting-like-its-calling-the-real-function