0

0

Vitest 中 spyOn 必须在测试作用域内声明的原因与配置冲突详解

心靈之曲

心靈之曲

发布时间:2026-01-24 14:37:12

|

195人浏览过

|

来源于php中文网

原创

Vitest 中 spyOn 必须在测试作用域内声明的原因与配置冲突详解

vitest 中将 `vi.spyon()` 提前声明在 `describe` 外会导致失效,根本原因在于 `mockreset`、`restoremocks`、`clearmocks` 和 `threads: false` 等配置会干扰全局 spy 的生命周期管理;正确做法是移除这些冲突配置,并始终在 `it` 内创建 spy。

在从 Jest 迁移到 Vitest 的过程中,一个常见且易被忽视的陷阱是:vi.spyOn() 不能安全地定义在测试用例(it/test)作用域之外——例如放在 describe 顶层或模块级。你遇到的现象(外部声明的 spy 始终不被调用、断言失败)并非 bug,而是 Vitest 模块隔离机制与特定测试配置共同作用的结果。

? 根本原因分析

Vitest 默认启用模块级隔离(module mocking),并在每个测试用例前后自动执行 mock 状态重置逻辑。当你启用以下配置时:

test: {
  mockReset: true,    // 每个测试前调用 vi.resetModules()
  restoreMocks: true, // 每个测试后恢复所有 mock 的原始实现
  clearMocks: true,   // 每个测试后清空所有 mock 调用记录(包括 spy)
  threads: false,     // 禁用多线程 → 强制串行执行,但加剧 mock 状态污染风险
}

这些选项会在每个 it 执行前后主动清理或重置所有已安装的 mock/spy。而你在 describe 外创建的 notificationSpy 属于“模块级 spy”,其引用在测试生命周期中被反复重置或销毁,导致后续 expect(notificationSpy).toHaveBeenCalledOnce(...) 实际检查的是一个已被清空(甚至重建)的 spy 实例 —— 因此永远无法捕获到调用。

✅ 正确行为:spy 应与测试用例强绑定,即在 it 内创建、使用、断言,确保其生命周期完全受当前测试控制。

✅ 推荐解决方案

1. 移除冲突配置(最直接有效)

根据你的验证结果,只需从 vite.config.ts 的 test 配置中删除以下四项:

一键职达
一键职达

AI全自动批量代投简历软件,自动浏览招聘网站从海量职位中用AI匹配职位并完成投递的全自动操作,真正实现'一键职达'的便捷体验。

下载
test: {
  // ❌ 删除以下四行(Vitest v1.3+ 默认行为已足够稳健)
  // mockReset: true,
  // restoreMocks: true,
  // clearMocks: true,
  // threads: false,

  globals: true,
  environment: 'jsdom',
  setupFiles: './vitest.setup.ts',
  include: ['**/*.{test,spec}.{ts,tsx,js,jsx}'],
  exclude: [...configDefaults.exclude, 'plop', './vitest.setup.ts'],
  deps: { inline: ['vitest-canvas-mock'] },
  testTimeout: 10000,
  alias: tsconfigPaths(),
  css: true,
}

✅ 移除后,Vitest 将采用更轻量、更符合直觉的默认 mock 行为:仅在 vi.mock() 显式调用时隔离模块,vi.spyOn() 则保持稳定,允许在 describe 中复用(但仍强烈建议在 it 内声明以保证可维护性)。

2. 最佳实践:始终在 it 内创建 spy(推荐)

即使配置已修正,也应坚持如下写法:

describe('PostboxList', () => {
  const renderComponent = (store: Store) => {
    render(, { store });
  };

  it('shows notification when fetching status is HasError', async () => {
    // ✅ 正确:spy 生命周期与测试完全对齐
    const notificationSpy = vi.spyOn(NotificationActions, 'addNotification');

    const store = mockStore({
      postbox: {
        documents: { data: [], fetchingStatus: DataFetchingStatus.HasError },
        messages: { data: [], fetchingStatus: DataFetchingStatus.HasError },
      },
    });

    renderComponent(store);

    expect(notificationSpy).toHaveBeenCalledOnce({
      title: 'POSTBOX.ERROR.TITLE',
      text: 'POSTBOX.ERROR.TEXT',
    });

    // ✅ 可选:显式恢复(增强健壮性,尤其在 `restoreMocks: false` 时)
    notificationSpy.mockRestore();
  });
});

⚠️ 注意事项

  • 不要依赖 beforeEach 创建跨测试的 spy:它仍可能被 clearMocks 清空;
  • 若必须复用 spy 逻辑(如多个测试需监听同一方法),可封装为工厂函数:
    const createNotificationSpy = () => vi.spyOn(NotificationActions, 'addNotification');
  • vi.restoreAllMocks() 应仅在 afterAll 或 teardown 中调用,避免干扰单个测试;
  • 启用 --run 模式(单次执行)可帮助排查是否为并发状态污染问题。

✅ 总结

场景 是否推荐 说明
vi.spyOn() 在 it 内声明 ✅ 强烈推荐 生命周期可控,兼容所有配置,语义清晰
vi.spyOn() 在 describe 外声明 ❌ 不推荐 易受 clearMocks/restoreMocks 干扰,迁移期高风险
启用 clearMocks: true + 全局 spy ❌ 避免 直接导致 spy 调用记录丢失,断言必然失败

遵循“spy 随测而生,随测而毁”原则,不仅能解决当前问题,更能提升测试的稳定性与可调试性。

相关专题

更多
c++ 根号
c++ 根号

本专题整合了c++根号相关教程,阅读专题下面的文章了解更多详细内容。

17

2026.01.23

c++空格相关教程合集
c++空格相关教程合集

本专题整合了c++空格相关教程,阅读专题下面的文章了解更多详细内容。

22

2026.01.23

yy漫画官方登录入口地址合集
yy漫画官方登录入口地址合集

本专题整合了yy漫画入口相关合集,阅读专题下面的文章了解更多详细内容。

91

2026.01.23

漫蛙最新入口地址汇总2026
漫蛙最新入口地址汇总2026

本专题整合了漫蛙最新入口地址大全,阅读专题下面的文章了解更多详细内容。

124

2026.01.23

C++ 高级模板编程与元编程
C++ 高级模板编程与元编程

本专题深入讲解 C++ 中的高级模板编程与元编程技术,涵盖模板特化、SFINAE、模板递归、类型萃取、编译时常量与计算、C++17 的折叠表达式与变长模板参数等。通过多个实际示例,帮助开发者掌握 如何利用 C++ 模板机制编写高效、可扩展的通用代码,并提升代码的灵活性与性能。

14

2026.01.23

php远程文件教程合集
php远程文件教程合集

本专题整合了php远程文件相关教程,阅读专题下面的文章了解更多详细内容。

65

2026.01.22

PHP后端开发相关内容汇总
PHP后端开发相关内容汇总

本专题整合了PHP后端开发相关内容,阅读专题下面的文章了解更多详细内容。

59

2026.01.22

php会话教程合集
php会话教程合集

本专题整合了php会话教程相关合集,阅读专题下面的文章了解更多详细内容。

61

2026.01.22

宝塔PHP8.4相关教程汇总
宝塔PHP8.4相关教程汇总

本专题整合了宝塔PHP8.4相关教程,阅读专题下面的文章了解更多详细内容。

33

2026.01.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Sass 教程
Sass 教程

共14课时 | 0.8万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

CSS教程
CSS教程

共754课时 | 23.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号