You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 20:00:02