使用Sinon模拟fastify-auth插件返回值失败问题排查
Sinon模拟fastify-auth验证函数失效的解决方法
问题原因
你的模拟代码不生效,核心是模块加载顺序和函数引用捕获导致的:
- 测试代码中,你先导入了
verifyGipToken模块,再导入fastify路由/插件,但此时gip-bearer-auth-plugin已经提前加载了原始的verifyGipToken函数引用,后续的stub无法覆盖已经被插件捕获的函数。 - Fastify注册插件时,
fastify.auth([verifyGipToken])会把当时的函数引用存入钩子,后续修改模块导出的函数不会影响这个已存储的引用。
解决方案
1. 调整模块导入顺序,先Stub再加载依赖模块
在测试中,必须先完成verifyGipToken的Stub,再导入任何依赖该函数的模块(比如fastify路由、认证插件),确保这些模块拿到的是Stub后的函数引用。
修改后的测试文件示例:
// test.js const t = require('tap') const sinon = require('sinon') // 1. 先导入要Stub的模块 const verifyGipTokenModule = require('/path/to/verifyGipToken') const sandbox = sinon.createSandbox() t.beforeEach(async t => { // 2. 完成Stub sandbox.stub(verifyGipTokenModule, 'verifyGipToken').callsFake(async req => { console.log('verifyGipToken stub called') return true }) // 3. 再导入Fastify应用/路由模块,确保Stub已生效 const buildApp = require('/path/to/your-fastify-app') t.context.app = await buildApp() // 假设你的应用导出的是创建实例的函数 }) t.afterEach(async t => { sandbox.restore() }) t.test('POST /v2/test 验证通过', async t => { // 4. 发送测试请求 const response = await t.context.app.inject({ method: 'POST', url: '/v2/test', headers: { // 可选:添加必要的请求头,比如Authorization } }) t.equal(response.statusCode, 200, '请求应成功返回') })
2. 确保Fastify应用的构建逻辑正确
如果你的Fastify应用是通过导出一个构建函数(而非直接创建实例),那么每次测试前构建实例时,都会使用最新的模块导出(也就是Stub后的函数)。比如调整你的应用入口:
// app.js(假设你的应用入口) const fastify = require('fastify') module.exports = async function buildApp() { const app = fastify() await app.register(require('./routes')) // 这里会加载路由和认证插件 return app }
备选方案:使用proxyquire替换模块
如果调整导入顺序不方便,可以用proxyquire直接在导入模块时替换verifyGipToken的实现:
// test.js const t = require('tap') const sinon = require('sinon') const proxyquire = require('proxyquire') t.test('test case', async t => { const verifyStub = sinon.stub().resolves(true) // 用proxyquire导入路由模块,替换verifyGipToken const buildApp = proxyquire('/path/to/your-fastify-app', { './share/helpers/verifyGipToken': { verifyGipToken: verifyStub } }) const app = await buildApp() const response = await app.inject({ method: 'POST', url: '/v2/test' }) t.ok(verifyStub.called, '验证函数应被调用') t.equal(response.statusCode, 200) })
关键注意点
- 必须保证Stub操作在任何依赖该函数的模块导入之前执行,Node.js的模块缓存机制会记住第一次导入的模块导出,后续导入不会重新加载。
- Fastify的钩子会捕获注册时的函数引用,所以要确保注册钩子时使用的是Stub后的函数。
内容的提问来源于stack exchange,提问作者Hamza Boularbah
相关产品推荐
相关产品推荐

