Why is my sinon stub acting like it's calling the real function?

你离开我真会死。 提交于 2020-08-20 10:33:05

问题


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

  1. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!