Jest测试时如何为每个用例获取axiosInstance的新实例?
问题根因
你遇到的问题本质是ES 模块的顶层初始化逻辑仅会在首次导入时执行一次:你的业务代码中const fetcher = axiosInstance()属于模块顶层代码,测试文件导入业务模块的瞬间就会执行该逻辑,生成固定的fetcher实例。后续你修改axiosInstance的mock实现,也不会改变已经创建完成的fetcher实例,因此失败用例依然会走首次的成功mock逻辑。
解决方法
下面提供3种可行的修复方案,你可以根据实际场景选择:
方案1:复用get的mock函数(改动最小,无需修改业务代码)
调整jest.mock的结构,把axios实例的get方法绑定为全局可修改的单一mock函数,后续测试用例直接修改该函数的返回行为即可:
// 测试文件顶部提前声明可复用的get mock const mockGet = jest.fn(); jest.mock('../../src/utils/network', () => { const axiosInstance = jest.fn().mockImplementation(() => { return { get: mockGet, // 所有axios实例的get都复用同一个mock函数 }; }) as any; axiosInstance.configure = jest.fn(); return axiosInstance; }); // 测试用例内直接修改mockGet的行为 describe('test LOGOUT', () => { beforeEach(() => { store.clearActions(); mockGet.mockReset(); // 每次用例前重置get的mock状态 }); it('should test be success', async () => { mockGet.mockResolvedValue({ headers: {} }); // 模拟成功返回 await store.dispatch({ type: 'LOGOUT', payload: { userName: 'testUserName' }, }); expect(store.getActions()).toContainEqual({ type: 'LOGOUT_SUCCESS' }); }); it('should test be fail', async () => { mockGet.mockRejectedValue(new Error('')); // 模拟失败抛出异常 await store.dispatch({ type: 'LOGOUT', payload: { userName: 'testUserName' }, }); expect(store.getActions()).toContainEqual({ type: 'LOGOUT_FAIL' }); }); });
方案2:重置模块缓存后动态导入(无需修改业务代码,适配复杂场景)
如果你不想调整mock结构,可以通过jest的模块重置能力,让每个测试用例都重新导入业务模块,重新生成fetcher实例:
describe('test LOGOUT', () => { let store; let fetchMiddleware; let axiosInstance; beforeEach(() => { jest.resetModules(); // 重置所有已导入模块的缓存 // 动态导入所有依赖,每次都会重新执行业务模块的顶层初始化逻辑 axiosInstance = require('../../src/utils/network').default; fetchMiddleware = require('../../src/你的中间件文件路径').default; const thunk = require('redux-thunk').default; const configureStore = require('redux-mock-store').default; const middlewares = [fetchMiddleware, thunk]; const mockStore = configureStore(middlewares); store = mockStore(getInitialReducerState()); store.clearActions(); }); // 测试用例保持原有逻辑即可,每次都会生成新的fetcher实例 it('should test be success', async () => { await store.dispatch({ type: 'LOGOUT', payload: { userName: 'testUserName' }, }); expect(store.getActions()).toContainEqual({ type: 'LOGOUT_SUCCESS' }); }); it('should test be fail', async () => { (axiosInstance as jest.Mock).mockImplementation(() => { return { get: jest.fn().mockImplementation(() => { throw new Error(' '); }), }; }); await store.dispatch({ type: 'LOGOUT', payload: { userName: 'testUserName' }, }); expect(store.getActions()).toContainEqual({ type: 'LOGOUT_FAIL' }); }); });
方案3:调整业务代码初始化时机(推荐,长期可维护性更高)
把fetcher的初始化从模块顶层移到中间件执行阶段,避免首次导入就生成固定实例:
// 业务代码修改后 import axiosInstance from '~/utils/network'; const fetchMiddleware = () => { const fetcher = axiosInstance(); // 每次中间件运行时再生成实例 switch (type) { case 'LOGOUT':{ try { await fetcher.get(API.GET.LOGOUT_OPERATION); dispatch({ type: 'LOGOUT_SUCCESS' }); } catch (err) { dispatch({ type: 'LOGOUT_FAIL' }); } } } }
修改后你的原有测试代码无需调整即可正常运行,该方案也方便后续其他场景的测试扩展。
内容的提问来源于stack exchange,提问作者Yoel
相关产品推荐
相关产品推荐

