Sinon存根被跳过作为节点表达中间件

Luk*_*gen 6 unit-testing mocha.js node.js sinon chai

我正在尝试测试特定路线的行为。即使我创建存根,它也会继续运行中间件。我希望事件认证暂时通过。我了解到,这并不是真正的“单元”测试。我快到那里了。我还简化了代码。这是要测试的代码:

const { rejectUnauthenticated } = require('../modules/event-authentication.middleware');

router.get('/event', rejectUnauthenticated, (req, res) => {
  res.sendStatus(200);
});
Run Code Online (Sandbox Code Playgroud)

这是我要跳过的中间件:

const rejectUnauthenticated = async (req, res, next) => {
  const { secretKey } = req.query;
  if (secretKey) {
    next();
  } else {
    res.status(403).send('Forbidden. Must include Secret Key for Event.');
  }
};

module.exports = {
  rejectUnauthenticated,
};
Run Code Online (Sandbox Code Playgroud)

测试文件:

const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');
let app;
const authenticationMiddleware = require('../server/modules/event-authentication.middleware');

const { expect } = chai;
chai.use(chaiHttp);

describe('with correct secret key', () => {
  it('should return bracket', (done) => {
    sinon.stub(authenticationMiddleware, 'rejectUnauthenticated')
      .callsFake(async (req, res, next) => next());

    app = require('../server/server.js');

    chai.request(app)
      .get('/code-championship/registrant/event')
      .end((err, response) => {
        expect(response).to.have.status(200);
        authenticationMiddleware.rejectUnauthenticated.restore();
        done();
      });
  });
});
Run Code Online (Sandbox Code Playgroud)

我已经尝试过以下类似的其他问题:如何在Express中模拟中间件以跳过单元测试的身份验证?和这个:节点表示es6 sinon存根中间件不起作用,但是我仍然从中间件中获取403,应该跳过它。我还以调试模式运行测试,因此我知道应该存根的中间件功能仍在运行。

这是存根我的代码的问题吗?这是ES6问题吗?

我可以重组代码或测试以使其正常工作吗?

Ser*_*pin 6

存根代码确实存在问题。

当您需要服务器文件时

const app = require('../server/server.js');
Run Code Online (Sandbox Code Playgroud)

您的应用是使用包括在内的整套中间件创建的,并且在中间rejectUnauthenticated存储了对中间件的引用app

当你做

sinon.stub(authenticationMiddleware, 'rejectUnauthenticated')
  .callsFake(async (req, res, next) => next());
Run Code Online (Sandbox Code Playgroud)

您将替换模块的rejectUnauthenticated导出方法authenticationMiddleware,而不是替换rejectUnauthenticated已存储的原始引用。

解决方案是模拟外部中间件方法创建应用程序(即require('../server/server.js');):

const chai = require('chai');
const chaiHttp = require('chai-http');
const sinon = require('sinon');

// don't create app right away
let app;
const authenticationMiddleware = require('../server/modules/event-authentication.middleware');

const { expect } = chai;
chai.use(chaiHttp);

describe('with correct secret key', () => {
  it('should return bracket', (done) => {
    sinon.stub(authenticationMiddleware, 'rejectUnauthenticated')
      .callsFake(async (req, res, next) => next());

    // method is stubbed, you can create app now
    app = require('../server/server.js');

    chai.request(app)
      .get('/code-championship/registrant/event')
      .end((err, response) => {
        expect(response).to.have.status(200);
        authenticationMiddleware.rejectUnauthenticated.restore();
        done();
      });
  });
});
Run Code Online (Sandbox Code Playgroud)

  • 是! 就是这样!当前文件中的所有测试都不是问题所在,但是来自其他需要`server.js`文件的文件的测试导致它损坏了!非常感谢! (4认同)
  • @LukeSchlangen可能会受到您在同一过程中运行的其他测试的影响-如果任何其他测试需要`server.js`,则节点对它的引用(以及中间件方法)也会被缓存,并且再次要求在测试中返回缓存的实例。我的建议是在要求`server.js`确保尚未缓存之前,单独运行测试,并在console.log`Object.keys(require.cache)`中运行。 (3认同)
  • @LukeSchlangen很酷,很乐意提供帮助。还有一个注释-使用.restore(),实际上并没有使所有内容恢复到初始状态,因为模块已被缓存-与您最初面临的问题相同,但是相反。在这种情况下,您可能想要使用一个在不同的上下文中运行每个文件的测试运行程序(afaik`jest`会这样做),或者利用诸如`decache`之类的工具每次重置节点模块缓存。 (2认同)