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

如何在Vitest/Jest中抽离测试Mock至独立文件复用?

Vitest 中复用 Hook Mock 的正确姿势

我在使用兼容Jest API的Vitest进行React项目测试时,遇到了Hook Mock复用的问题:

现有文件结构:

// hooks/useEntities/useEntities.ts
function useEntities() {
    // 该Hook会发送API请求,需要在测试中Mock
}
// SomeComponent.tsx
import useEntities from 'hooks/useEntities/useEntities'

function SomeComponent () {
    const entities = useEntities()

    return <div>...</div>
}

直接在测试文件里写vi.mock是可行的:

// SomeComponent.test.tsx
vi.mock('hooks/useEntities/useEntities', () => ({ // 此方式有效
    default: [...]
}))

describe('SomeComponent', () => {
    it('renders properly', () => {
        render(<SomeComponent />)
    })
})

但因为要Mock大量Hook,想把Mock逻辑抽离到独立文件复用,尝试了两种方式都失败了:
第一种:

// init.ts
vi.mock('hooks/useEntities/useEntities', () => ({
    default: [...]
}))
// SomeComponent.test.tsx => 无效
await init()

describe(...) {
    it(...) {
        render(<SomeComponent />)
    }
}

第二种:

// init.ts
vi.mock('hooks/useEntities/useEntities', () => ({
    default: [...]
}))
// SomeComponent.test.tsx => 无效
describe(...) {
    beforeAll(async () => {
        await init()
        await init(vi) // 尝试传入vi实例
    })
    it(...) {
        render(<SomeComponent />)
    }
}

问题原因

Vitest的vi.mock是静态执行机制,它会在测试文件的所有代码(包括import语句)执行前就运行,所以不能放在异步函数、beforeAll这类生命周期钩子里面调用,必须让它在模块顶层执行才能生效。

正确的复用方案

方案1:集中式静态Mock(适合固定返回值的场景)

  1. 创建一个专门的Mock配置文件,在文件顶层直接编写所有Hook的Mock:
// test/mocks/hooks.ts
import { vi } from 'vitest';

// 集中管理所有需要Mock的Hook
vi.mock('hooks/useEntities/useEntities', () => ({
  default: () => [/* 你的固定Mock返回值 */]
}));

// 可以继续添加其他Hook的Mock
// vi.mock('hooks/useUser/useUser', () => ({ ... }))
  1. 在测试文件的最顶层导入这个配置文件(必须放在其他import之前):
// SomeComponent.test.tsx
// 先导入Mock配置,确保静态Mock先执行
import '../test/mocks/hooks';

import { render } from '@testing-library/react';
import SomeComponent from './SomeComponent';

describe('SomeComponent', () => {
  it('renders correctly with mocked hook', () => {
    render(<SomeComponent />);
    // 你的断言逻辑
  });
});

方案2:可动态修改的Mock(适合不同用例需要不同返回值的场景)

如果需要在不同测试用例里修改Hook的返回值,可以把Mock函数暴露出来:

  1. 在Mock配置文件中定义可修改的Mock函数并暴露:
// test/mocks/hooks.ts
import { vi } from 'vitest';

// 创建一个可修改的Mock函数,设置默认返回值
export const mockUseEntities = vi.fn(() => [/* 默认返回值 */]);

vi.mock('hooks/useEntities/useEntities', () => ({
  default: mockUseEntities
}));
  1. 在测试用例中导入并修改Mock函数:
// SomeComponent.test.tsx
import '../test/mocks/hooks';
import { mockUseEntities } from '../test/mocks/hooks';
import { render } from '@testing-library/react';
import SomeComponent from './SomeComponent';

describe('SomeComponent', () => {
  // 每个用例前重置Mock状态,避免用例间互相影响
  beforeEach(() => {
    mockUseEntities.mockReset();
  });

  it('显示空状态当没有数据时', () => {
    mockUseEntities.mockReturnValue([]);
    render(<SomeComponent />);
    // 断言空状态的逻辑
  });

  it('渲染实体列表当有数据时', () => {
    mockUseEntities.mockReturnValue([{ id: 1, name: '测试实体' }]);
    render(<SomeComponent />);
    // 断言列表渲染的逻辑
  });
});

内容的提问来源于stack exchange,提问作者Matan Gubkin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 05:50:22