从Jest迁移到Vitest:spyOn在测试用例外部失效问题
问题:Vitest中全局声明的spyOn无法正常工作,移至测试用例内部则恢复正常
现象
将vi.spyOn声明在测试用例作用域之外时,断言spy的调用情况会失败:
const notificationSpy = vi.spyOn(NotificationActions, 'addNotification'); describe('PostboxList', () => { const renderComponent = (store: Store) => { render(<PostboxList />, { store, }); }; it('the notification is visible when fetching status is HasError', async () => { const store = mockStore({ postbox: { documents: { data: [], fetchingStatus: DataFetchingStatus.HasError, }, messages: { data: [], fetchingStatus: DataFetchingStatus.HasError, }, }, }); renderComponent(store); expect(notificationSpy).toHaveBeenCalledOnce({ title: 'POSTBOX.ERROR.TITLE', text: 'POSTBOX.ERROR.TEXT', }); }); });
但将vi.spyOn移到测试用例(it块)内部后,测试就能通过:
describe('PostboxList', () => { const renderComponent = (store: Store) => { render(<PostboxList />, { store, }); }; it('the notification is visible when fetching status is HasError', async () => { const notificationSpy = vi.spyOn(NotificationActions, 'addNotification'); const store = mockStore({ postbox: { documents: { data: [], fetchingStatus: DataFetchingStatus.HasError, }, messages: { data: [], fetchingStatus: DataFetchingStatus.HasError, }, }, }); renderComponent(store); expect(notificationSpy).toHaveBeenCalledOnce({ title: 'POSTBOX.ERROR.TITLE', text: 'POSTBOX.ERROR.TEXT', }); }); });
当前Vitest配置的test选项:
test: { globals: true, environment: 'jsdom', setupFiles: './vitest.setup.ts', include: ['**/*.{test, spec}.{ts,tsx,js,jsx}'], exclude: [...configDefaults.exclude, 'plop', './vitest.setup.ts'], deps: { inline: ['vitest-canvas-mock'], }, testTimeout: 10000, threads: false, alias: tsconfigPaths(), mockReset: true, restoreMocks: true, clearMocks: true, css: true, }
已尝试在beforeEach/afterEach中手动重置mock和spy,但未解决问题。
原因分析
你配置中的restoreMocks: true和clearMocks: true会在每个测试用例执行前后自动重置/清除mock:
restoreMocks: true会在每个测试后将spy恢复为原始实现,同时清除调用记录clearMocks: true会在每个测试前清除mock的调用记录
当spy声明在全局作用域时,初始化阶段创建的spy会被这些自动重置操作影响:要么调用记录被提前清空,要么spy被恢复为原始方法,导致断言时无法捕获到目标调用。而将spy放在测试用例内部时,每次测试都会重新创建spy,避开了全局重置的影响,确保断言时使用的是当前测试周期内有效的spy实例。
解决方案
保持spy在测试用例内部声明:这是最稳妥的方式,确保每个测试用例都使用独立的spy实例,完全避开全局重置机制的干扰。
使用beforeEach批量创建spy:如果多个测试用例需要复用同一个spy逻辑,可以在
beforeEach中创建,确保每个测试用例执行前都生成新的spy实例:describe('PostboxList', () => { let notificationSpy: ReturnType<typeof vi.spyOn>; beforeEach(() => { notificationSpy = vi.spyOn(NotificationActions, 'addNotification'); }); // ...测试用例 });调整Vitest配置(不推荐):如果一定要在全局作用域声明spy,可以关闭自动重置相关选项,但需手动管理mock生命周期避免测试间状态污染:
test: { // ...其他配置 mockReset: false, restoreMocks: false, clearMocks: false, }同时在
afterEach中手动重置spy:afterEach(() => { notificationSpy.mockReset(); });
内容的提问来源于stack exchange,提问作者cassmtnr
相关产品推荐
相关产品推荐

