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

React Vitest测试中Mock未生效,实际调用真实API

Vitest Mock第三方API失效(真实API被调用)的排查与修复

1. 检查Mock时机与模块导入顺序

Vitest的vi.mock必须在组件导入前执行,否则组件会先加载真实模块,Mock无法生效。

  • 错误写法(先导入组件,后Mock):
import { render, screen } from '@testing-library/react';
import App from './App'; // 真实模块已被加载,Mock无效
vi.mock('./SnackOrBoozeApi', () => ({
  getGoodies: vi.fn().mockResolvedValue({ snacks: [], drinks: [] })
}));
  • 正确写法(先Mock,再导入组件):
import { vi } from 'vitest';
import { render, screen } from '@testing-library/react';
// 先Mock目标模块
vi.mock('./SnackOrBoozeApi', () => ({
  getGoodies: vi.fn().mockResolvedValue({
    snacks: [{id:1, name:'Mock Snack1'}, {id:2, name:'Mock Snack2'}],
    drinks: [{id:1, name:'Mock Drink1'}, {id:2, name:'Mock Drink2'}]
  })
}));
// Mock完成后再导入组件
import App from './App';

2. 确认Mock路径与组件导入路径完全匹配

如果组件中导入API的路径是../api/SnackOrBoozeApi,而测试中Mock的是./SnackOrBoozeApi,会导致Mock失效。可以打印导入的模块验证:

import * as SnackOrBoozeApi from './SnackOrBoozeApi';
console.log(SnackOrBoozeApi.getGoodies); // 输出应为Mock函数,而非真实函数

3. 匹配API的导出方式

  • 如果组件用默认导出导入API:
// 组件中:import SnackOrBoozeApi from './SnackOrBoozeApi'
vi.mock('./SnackOrBoozeApi', () => ({
  default: {
    getGoodies: vi.fn().mockResolvedValue(...)
  }
}));
  • 如果是命名导出,直接Mock命名对象即可。

4. 测试用例中等待异步数据加载

即使Mock成功,也要用异步查询方法等待数据渲染完成,不能直接用getBy*:

test('renders mock snacks and drinks', async () => {
  render(<App />);
  // 用findBy*或waitFor等待Mock数据渲染
  const snackCount = await screen.findByText(/Snacks: 2/i);
  const drinkCount = await screen.findByText(/Drinks: 2/i);
  expect(snackCount).toBeInTheDocument();
  expect(drinkCount).toBeInTheDocument();
  // 验证Mock函数被调用
  expect(SnackOrBoozeApi.getGoodies).toHaveBeenCalled();
});

5. 重置模块缓存解决跨用例污染

如果多个测试用例共享模块,可能导致Mock失效,在beforeEach中重置模块:

beforeEach(() => {
  vi.resetModules();
  vi.mock('./SnackOrBoozeApi', () => ({
    getGoodies: vi.fn().mockResolvedValue(...)
  }));
});

6. 修复快照测试

必须等待Mock数据渲染完成后再生成快照:

test('matches snapshot with mock data', async () => {
  const { container } = render(<App />);
  await waitFor(() => screen.getByText(/Snacks: 2/i));
  expect(container).toMatchSnapshot();
});

内容的提问来源于stack exchange,提问作者Camden Tadhg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 05:59:53