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

Vitest测试中Mock自定义Hook未返回预期值问题

解决Vitest中Mock自定义Hook后组件未获取到Mock值的问题

问题重现

我有三个文件:

  • use-user-info.ts
  • user-info.tsx
  • user-info.test.tsx

想要测试user-info.tsx中的UserInfo组件,在测试文件中Mock了useUserInfo Hook:

vi.mock("@/use-user-info");
describe(() => {
 beforeEach(() => {
  vi.mocked("@/use-user-info").mockReturnValue({
   userId: 1,
   username: "John Doe",
   isLoading: false
  })
  return () => {vi.clearAllMock()}
 })
})

遇到的问题:在beforeEach作用域中调用该Hook能拿到Mock值,但user-info.tsx中通过console.log查看Hook返回值时,得到的却是Hook的默认返回值。尝试了vi.mock、vi.doMock、mockReturnValue、mockImplementation等多种Mock方式,其他类似Hook也存在相同问题。

use-user-info.ts的实现:

export const useUserInfo = (userId) => {
 const { infoNotification } = useNotification();
 const { updateUser } = useUpdateUser(updateBudgetData)
 const userMessages = useUserMessages(userId);

 return {
  infoNotification,
  updateUser,
  userMessages
 }
}

问题原因及解决方案

1. Mock路径与组件导入路径不匹配

Vitest是根据导入路径匹配Mock的,如果组件中导入useUserInfo的路径和测试文件中vi.mock的路径不一致,Mock就不会生效。比如组件用相对路径../hooks/use-user-info,测试却用别名@/use-user-info,两者会被视为不同模块,导致Mock失效。

解决:
检查user-info.tsx中useUserInfo的导入语句,确保测试文件中vi.mock的路径和它完全一致。若使用路径别名,需确保Vitest配置中正确解析别名(参考下文配置示例)。

2. 错误使用vi.mocked

vi.mocked应该包裹导入的Hook函数,而非直接包裹模块路径。之前的写法vi.mocked("@/use-user-info")是错误的,正确做法是先导入Hook,再用vi.mocked包裹这个导入的函数。

修正后的测试代码示例:

// 先导入需要Mock的Hook和组件
import { useUserInfo } from "@/use-user-info";
import UserInfo from "@/user-info";

// 顶层调用vi.mock,会自动被提升到文件顶部
vi.mock("@/use-user-info");

describe("UserInfo Component", () => {
  beforeEach(() => {
    // Mock返回值必须和实际Hook的返回结构一致
    vi.mocked(useUserInfo).mockReturnValue({
      infoNotification: { add: vi.fn() }, // 模拟infoNotification的结构
      updateUser: vi.fn(), // 用vi.fn()模拟函数类型属性
      userMessages: [] // 模拟用户消息数据
    });
  });

  afterEach(() => {
    // 注意是clearAllMocks(复数),清理所有Mock的调用记录
    vi.clearAllMocks();
  });

  it("应该正确渲染Mock的用户数据", () => {
    render(<UserInfo />);
    // 这里添加你的断言逻辑,比如检查组件是否渲染了预期内容
  });
});

3. Mock返回结构与实际Hook不匹配

你的useUserInfo返回的是包含infoNotification、updateUser、userMessages的对象,但之前的Mock返回的是userId、username、isLoading,结构完全不匹配。即使Mock生效,组件也无法拿到预期属性,甚至引发错误,也会让你误以为Mock未生效。

解决:
严格按照实际Hook的返回结构编写Mock返回值,确保每个属性都存在(用vi.fn()模拟函数类型属性,用合适默认值模拟其他属性)。

4. 路径别名未在Vitest中配置

如果使用@作为路径别名,需要在Vitest配置文件中明确配置别名解析,否则测试环境无法正确识别@/use-user-info这类路径,导致Mock失效。

Vitest配置示例(vitest.config.ts):

import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src') // 映射@到src目录
    }
  },
  test: {
    environment: 'jsdom', // React组件测试需要jsdom环境
    // 其他测试配置项
  }
});

5. Mock时机错误

vi.mock会被Vitest自动提升到文件顶部执行,所以不要在describe、it或条件语句中调用vi.mock,必须放在文件顶层,否则可能导致Mock未提前生效,组件加载时还是会引入原始Hook。

内容的提问来源于stack exchange,提问作者Julio Villanueva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:05:19