使用Sinon Stub测试带中间件保护的Express.js路由遇阻
解决Express.js路由测试中Sinon Stub认证中间件失效的问题
我看到你在测试受isAdmin中间件保护的路由时遇到了Stub被忽略的问题,报错TypeError: Cannot read property 'user' of undefined,这说明原始的中间件仍在执行,而你的Stub根本没生效。问题根源在于Node.js的模块缓存机制,下面给你详细分析和解决办法:
问题原因
当你加载Express应用(server)时,它会导入路由模块,而路由模块已经提前导入并缓存了原始的isAdmin中间件引用。之后你在测试中Stub的只是测试文件里的auth.isAdmin,但路由里使用的还是缓存的原始中间件,所以Stub完全被忽略,原始中间件执行时找不到req.decoded.user就抛出了错误。
解决方案
方案1:清除模块缓存,强制重新加载模块
在beforeEach中先删除isAdmin和app的模块缓存,这样每次测试都会重新加载这些模块,路由就能使用Stub后的中间件了。修改你的beforeEach代码:
beforeEach(() => { // 删除模块缓存,避免复用之前加载的版本 delete require.cache[require.resolve("../../../middlewares/isAdmin")]; delete require.cache[require.resolve("../../../app")]; // 重新加载isAdmin模块并Stub auth = require("../../../middlewares/isAdmin"); sinon.stub(auth, "isAdmin").callsFake((req, res, next) => { console.log("Stubbed"); return next(); }); // 重新加载server,此时路由会使用Stub后的中间件 server = require("../../../app"); });
方案2:使用proxyquire替换模块导出(更灵活的方式)
如果清除缓存的方式不符合你的测试场景,可以用proxyquire工具直接替换中间件的导出。
首先安装依赖:
npm install proxyquire --save-dev
然后修改测试文件:
import chai from "chai"; import chaiHttp from "chai-http"; import sinon from "sinon"; import proxyquire from "proxyquire"; import { country } from "./addcountry-data"; let server; const { expect } = chai; chai.should(); chai.use(chaiHttp); describe("Admin user can add country", () => { let stubbedIsAdmin; beforeEach(() => { // 创建Stub的isAdmin中间件 stubbedIsAdmin = sinon.stub().callsFake((req, res, next) => { console.log("Stubbed"); return next(); }); // 使用proxyquire加载app,替换isAdmin中间件 server = proxyquire("../../../app", { "./middlewares/isAdmin": { isAdmin: stubbedIsAdmin } }); }); it("should allow user with admin role add a country", done => { chai.request(server) .post("/api/v1/admin/addcountry") .send(country) .end((err, res) => { expect(res).to.have.status(201); // 验证Stub确实被调用过 expect(stubbedIsAdmin.calledOnce).to.be.true; done(); }); }); });
额外提示
- 确保测试中没有其他地方提前加载
app或isAdmin模块,否则缓存会干扰Stub效果。 - 如果后续控制器需要
req.decoded的值,你可以在Stub的中间件里给req.decoded赋值,比如:sinon.stub(auth, "isAdmin").callsFake((req, res, next) => { req.decoded = { user: { role: "Admin" } }; // 模拟解码后的用户信息 return next(); });
内容的提问来源于stack exchange,提问作者Fiyin Akinsiku
相关产品推荐
相关产品推荐

