在不创建重新创建服务器的情况下测试 Express 中间件的简单方法?

Simple way to test middleware in Express without creating recreating server?

我希望能够在每次测试的基础上存根我的中间件函数。正如所述,问题是我不能只存根我的中间件函数,因为节点已经缓存了中间件函数,所以我不能存根,因为我在一开始就创建了我的应用程序。

const request = require("supertest");
const { expect } = require("chai");
const sinon = require('sinon');
const auth = require ("../utils/auth-middleware")
const adminStub = sinon.stub(auth, "isAdmin").callsFake((req, res, next) => next());
const app = require("../app.js"); // As soon as I create this, the middleware isAdmin function becomes cached and no longer mutable

以上作为链接的 SO 答案中描述的解决方案,但我不喜欢那样为了恢复存根或修改假的,我必须完全重新创建服务器。

我想知道是否有更好、更优雅的方法来解决 Node 首先缓存这些函数的事实 require。我正在考虑使用 proxyquiredecache,但两者似乎都提供了解决方法而不是可持续的解决方案(尽管我在这里很可能是错误的)。

这个问题与 Node 缓存模块并没有真正的关系 - 它是在最初创建服务器时存储对中间件函数的引用的明确表示。 在 required 模块的 isAdmin 方法被存根后,它是被存根的缓存版本,所以使用像 proxyquire 这样的工具只允许你需要一个新版本的模块(没有存根方法)如果你出于某种原因需要它。

如果您正在寻找的是为已创建的快速服务器调整特定中间件的行为,您需要一种方法来通过其引用来更改中间件功能的行为。 希望 sinon 存根(以及其他存根,例如开玩笑提供的存根)能够做到这一点。但是,您仍然需要在创建快速服务器之前存根模块,以便它存储对存根函数的引用。

示例实现可能如下所示:

const request = require("supertest");
const { expect } = require("chai");
const sinon = require('sinon');
const auth = require ("../utils/auth-middleware");

// store reference to original function in case you need it:
const originalIsAdmin = auth.isAdmin

// replace isAdmin method with a stubbed, but don't specify implementation yet
const adminStub = sinon.stub(auth, "isAdmin");

// init express server that relies on stubbed `auth.isAdmin` reference
const app = require("../app.js");

it('this test is using auth.isAdmin that just calls .next()', () => {
  // make middleware just pass
  auth.isAdmin.callsFake((req, res, next) => next());

  // ...
});

it('this test is using real auth.isAdmin implementation', () => {
  // make middleware call real implementation
  auth.isAdmin.callsFake(originalIsAdmin);

  // ...
});

Sergey Lapin 解决方案非常适合我进行单文件测试。 如果你是 运行 多个测试文件(例如 mocha)并且其中之一像 Sergey Lapin 建议的那样对中间件进行存根 - callsFake 不起作用。

解决方法: test_config.js

const auth= require('../utils/auth-middleware');
const sinon = require('sinon');

const originalIsAdmin = auth.isAdmin;
const stubbedIsAuth = sinon.stub(auth, 'isAdmin');

const app = require("../app.js");

module.exports = {
    originalIsAdmin,
    stubbedIsAuth,
    app,
};

内部文件 A.js 你需要跳过的是 isAdmin:

const { app } = require('./test_config');
const auth= require('../utils/auth-middleware');

// before each test. No other .stub
beforeEach(function () {
            auth.isAdmin.callsFake((req, res, next) => next());
});

内部文件 B.js 你需要 isAdmin 的原始实现的地方:

const { originalIsAdmin, app} = require('./test_config');

// before each test. No other .stub
beforeEach(function () {
            auth.isAdmin.callsFake(originalIsAdmin);
});