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

Javascript 在Express中测试中间件而不创建重新创建服务器的简单方法?,javascript,node.js,mocha.js,sinon,Javascript,Node.js,Mocha.js,Sinon,我希望能够在每次测试的基础上存根我的中间件功能。正如所阐述的,问题在于我不能只存根我的中间件函数,因为node缓存了中间件函数,所以我不能存根它,因为我在一开始就创建了我的应用程序 const request = require("supertest"); const { expect } = require("chai"); const sinon = require('sinon'); const auth = require ("../utils/auth-middleware") con

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

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
时缓存这些函数这一事实。我在考虑可能使用
proxyquire
decache
,但两者似乎都提供了解决办法,而不是可持续的解决方案(尽管我在这里很可能是错的).

问题实际上与节点缓存模块无关,而是express在最初创建服务器时存储对中间件功能的引用。 在
isAdmin
方法的
require
d模块被存根后,是缓存的版本被存根,因此如果出于某种原因需要,使用
proxyquire
这样的工具只允许您需要模块的新版本(没有存根方法)

如果您正在寻找的是为已经创建的express server调整特定中间件的行为,那么您需要一种通过引用来改变中间件功能行为的方法。 希望sinon存根(以及其他存根,例如jest提供的存根)能够做到这一点。但是,您仍然需要在创建express server之前对模块进行存根处理,以便它存储对存根函数的引用

示例实现可能如下所示:

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);

  // ...
});

问题实际上与节点缓存模块无关,而是express在最初创建服务器时存储对中间件函数的引用。 在
isAdmin
方法的
require
d模块被存根后,是缓存的版本被存根,因此如果出于某种原因需要,使用
proxyquire
这样的工具只允许您需要模块的新版本(没有存根方法)

如果您正在寻找的是为已经创建的express server调整特定中间件的行为,那么您需要一种通过引用来改变中间件功能行为的方法。 希望sinon存根(以及其他存根,例如jest提供的存根)能够做到这一点。但是,您仍然需要在创建express server之前对模块进行存根处理,以便它存储对存根函数的引用

示例实现可能如下所示:

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);
});

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);
});