Vitest跨测试缓存Mock导致reactfire模拟无法覆盖问题
解决Vitest中reactfire Mock无法覆盖的问题
问题根源
vi.mock 是Vitest的静态Mock机制,会被自动提升到测试文件的最顶部执行。如果你的组件是静态导入(在文件顶部用import引入),组件会在测试运行前就加载并缓存第一次的Mock结果,后续测试中修改的Mock不会生效。即便调用vi.resetModules(),也无法清除已静态导入组件的缓存。
解决方案
方案1:动态导入组件 + 单测独立Mock
把组件的导入从顶部移到每个测试中,配合vi.resetModules()确保每次测试都重新加载组件和对应Mock:
function getMockedAuth(type: 'admin' | 'user') { return { useUser: () => ({ data: { uid: type, }, }), useFirebaseApp: () => ({ auth: () => ({ currentUser: { uid: type, }, }), }), }; } beforeEach(() => { vi.resetModules(); vi.clearAllMocks(); cleanup(); }); describe('DashboardPage', () => { test('should render Backlog if user is admin', async () => { vi.mock('reactfire', () => getMockedAuth('admin')); // 动态导入组件,确保每次测试都重新加载 const { DashboardPage } = await import('./DashboardPage'); render(<DashboardPage />); expect(screen.getByText('Backlog')).toBeTruthy(); }); test('should not render Backlog if user is not admin', async () => { vi.mock('reactfire', () => getMockedAuth('user')); const { DashboardPage } = await import('./DashboardPage'); render(<DashboardPage />); expect(screen.queryByText('Backlog')).toBeNull(); }); });
方案2:使用可动态修改的Mock函数
在文件顶部定义一次Mock,用vi.fn()创建可修改的Mock函数,然后在每个测试中更新返回值:
import { render, screen, cleanup } from '@testing-library/react'; import vi from 'vitest'; // 定义可动态修改的用户类型变量 let mockUserType: 'admin' | 'user'; // 顶部创建一次Mock,返回值依赖变量 vi.mock('reactfire', () => ({ useUser: vi.fn(() => ({ data: { uid: mockUserType, }, })), useFirebaseApp: vi.fn(() => ({ auth: vi.fn(() => ({ currentUser: { uid: mockUserType, }, })), })), })); // 静态导入组件即可,因为Mock是动态的 import { DashboardPage } from './DashboardPage'; beforeEach(() => { vi.clearAllMocks(); cleanup(); // 重置默认用户类型 mockUserType = 'admin'; }); describe('DashboardPage', () => { test('should render Backlog if user is admin', () => { mockUserType = 'admin'; render(<DashboardPage />); expect(screen.getByText('Backlog')).toBeTruthy(); }); test('should not render Backlog if user is not admin', () => { mockUserType = 'user'; render(<DashboardPage />); expect(screen.queryByText('Backlog')).toBeNull(); }); });
方案3:用vi.importMock动态修改Mock
通过vi.importMock获取Mock模块的引用,直接在测试中修改Mock函数的返回值:
import { render, screen, cleanup } from '@testing-library/react'; import vi from 'vitest'; import { DashboardPage } from './DashboardPage'; beforeEach(() => { vi.resetModules(); vi.clearAllMocks(); cleanup(); }); describe('DashboardPage', () => { test('should render Backlog if user is admin', async () => { // 获取reactfire的Mock引用 const reactfireMock = await vi.importMock('reactfire') as typeof import('reactfire'); // 更新Mock返回值 reactfireMock.useUser.mockReturnValue({ data: { uid: 'admin' } }); reactfireMock.useFirebaseApp.mockReturnValue({ auth: () => ({ currentUser: { uid: 'admin' } }) }); render(<DashboardPage />); expect(screen.getByText('Backlog')).toBeTruthy(); }); test('should not render Backlog if user is not admin', async () => { const reactfireMock = await vi.importMock('reactfire') as typeof import('reactfire'); reactfireMock.useUser.mockReturnValue({ data: { uid: 'user' } }); reactfireMock.useFirebaseApp.mockReturnValue({ auth: () => ({ currentUser: { uid: 'user' } }) }); render(<DashboardPage />); expect(screen.queryByText('Backlog')).toBeNull(); }); });
内容的提问来源于stack exchange,提问作者cerealex
相关产品推荐
相关产品推荐

