如何在Jest自定义环境文件中Mock服务端路由依赖的中间件函数
问题核心原因
- 自定义
NodeEnvironment运行在jest主进程上下文,未注入jest全局对象,因此直接调用jest.mock会报错。 - 服务实例在
setup阶段已经完成初始化,所有中间件的引用已经被加载固定,测试文件执行时再调用jest.mock无法替换已经存在的中间件实例,因此Mock不会生效。
可行实现方案
方案1:全局统一Mock(所有测试共用相同Mock实现)
适合所有测试都需要同一套Mock中间件的场景,直接在自定义环境中通过改写require缓存实现Mock,无需使用jest.mock:
const NodeEnvironment = require('jest-environment-node') const supertest = require('supertest') // 先改写需要Mock的中间件的模块缓存,再加载server // 示例:Mock src/middlewares/auth.js 中间件 const mockAuthMiddleware = async (ctx, next) => { ctx.state.user = { id: 1, role: 'admin' } // 你的Mock逻辑 await next() } require.cache[require.resolve('../src/middlewares/auth.js')] = { exports: mockAuthMiddleware } // 此时加载的server会自动使用上面Mock的中间件 const { app, init } = require('../src/server') class ServerEnvironment extends NodeEnvironment { constructor(config, context) { super(config, context) this.testPath = context.testPath } async setup() { await init this.server = app.listen() this.api = supertest(this.server) this.global.api = this.api } async teardown() { this.server.close() } } module.exports = ServerEnvironment
方案2:单测试文件定制Mock(不同测试可使用不同Mock实现)
适合不同测试需要不同Mock逻辑的场景,给中间件增加运行时代理层即可实现动态切换:
- 调整服务端中间件注册逻辑,增加全局Mock兜底:
// src/server.js 中修改需要支持Mock的中间件注册部分 const authMiddleware = require('./middlewares/auth') // 增加全局Mock判断,存在则优先使用Mock实现 const useMock = global.__SERVER_MOCKS__?.auth ?? authMiddleware app.use(useMock)
- 修改自定义环境,暴露全局Mock设置方法:
class ServerEnvironment extends NodeEnvironment { constructor(config, context) { super(config, context) this.testPath = context.testPath } async setup() { await init // 初始化全局Mock存储对象 this.global.__SERVER_MOCKS__ = {} // 暴露设置Mock的全局方法 this.global.setServerMock = (middlewareName, mockImpl) => { this.global.__SERVER_MOCKS__[middlewareName] = mockImpl } // 暴露清除Mock的全局方法 this.global.clearServerMocks = () => { this.global.__SERVER_MOCKS__ = {} } this.server = app.listen() this.api = supertest(this.server) this.global.api = this.api } async teardown() { this.server.close() } }
- 在测试文件中直接调用全局方法设置Mock即可生效:
// 某测试文件示例 describe('测试需要Mock鉴权的接口', () => { beforeAll(() => { // 自定义当前测试需要的Mock逻辑 setServerMock('auth', async (ctx, next) => { ctx.state.user = { id: 2, role: 'guest' } await next() }) }) afterAll(() => { clearServerMocks() }) test('测试游客权限接口', async () => { const res = await api.get('/guest/api') expect(res.statusCode).toBe(200) }) })
注:该方案利用中间件每次请求触发时才执行的特性,无需重启服务即可动态切换Mock实现,性能开销极低。
内容的提问来源于stack exchange,提问作者MarcusDevAccount
相关产品推荐
相关产品推荐

