首页
/ Angular 测试框架迁移指南:从 Karma 与 Jasmine 平滑切换到 Vitest

Angular 测试框架迁移指南:从 Karma 与 Jasmine 平滑切换到 Vitest

2026-09-07 14:32:12作者:宣海椒Queenly

本文以 Angular 官方文档(migrating-to-vitest.md)为骨架,结合本仓库中 Angular 核心代码(尤其是 zone.js 提供的 Vitest 补丁实现),系统讲解如何把既有 Angular 项目的单元测试从 Karma + Jasmine 迁移到 Vitest:涵盖手工迁移的五个步骤、可选的浏览器(真实浏览器)运行模式、由 Angular CLI 提供的 refactor-jasmine-vitest 自动化重构 schematic、自定义 Vitest 配置,以及 zone.js Vitest patch 的作用与原理。读完本文,你将能够独立完成一次完整的迁移并规避其中的常见坑点。

重要前提:将既有项目迁移到 Vitest 目前仍被视为实验性能力;此外该流程要求项目使用 application 构建体系(application build system),这也是所有新创建项目的默认配置。新项目默认使用 Vitest 作为单元测试运行器,而存量项目则仍默认使用 Karma。


一、迁移前必读:Karma → Vitest 的整体思路

Angular CLI 已经将 Vitest 作为新项目的默认单元测试运行器。对于老项目而言,迁移的本质分为两条并行主线:

  1. 运行器层迁移:把 angular.jsontest target 的 builder 从 @angular/build:karma 换成 @angular/build:unit-test,让测试走 Vitest;
  2. 测试代码层迁移:把测试文件(.spec.ts)里基于 Jasmine 全局 API 的写法(spyOnjasmine.objectContainingfit/fdescribe 等)改写为 Vitest 等价写法。

第一条主线依赖 Angular 应用构建体系(application builder)——只有新构建体系才能被 unit-test builder 复用其编译产物;第二条主线可以手工完成,也可以交给官方实验性 schematic refactor-jasmine-vitest 自动完成。

从本仓库的文档结构可以看到 Vitest 相关的完整测试知识地图:guide/testing/overview.md 综述各类测试场景、components-scenarios.md 提供组件测试场景与基于 Vitest fake timers 的异步测试示例、code-coverage.md 说明覆盖率能力,而本文对应的 karma.md 则是尚未迁移用户仍在使用的 Karma 指南。


二、手工迁移步骤(Manual migration steps)

在运行自动化重构 schematic 之前,必须先把项目手工切换到 Vitest 测试运行器。整个过程分为 5 个步骤。

第 1 步:安装依赖

安装 vitest 以及一个 DOM 模拟库。虽然仍可在真实浏览器中测试(见第 5 步),但 Vitest 默认会在 Node.js 中通过 DOM 模拟库来模拟浏览器环境,从而获得更快的执行速度。Angular CLI 会自动探测环境:如果安装了 happy-dom 则优先使用,否则回退到 jsdom——因此这两个包至少必须安装其一

# npm
npm install --save-dev vitest jsdom

# yarn
yarn add --dev vitest jsdom

# pnpm
pnpm add -D vitest jsdom

# bun
bun add --dev vitest jsdom

提示:想追求更轻量、更快的 DOM 模拟,可安装 happy-dom 替代 jsdom;安装 happy-dom 后 CLI 会自动优先选择它。

第 2 步:更新 angular.json

找到项目对应的 test target,把 builder 改为 @angular/build:unit-test

{
  "projects": {
    "your-project-name": {
      "architect": {
        "test": {
          "builder": "@angular/build:unit-test"
        }
      }
    }
  }
}

unit-test builder 有以下默认值,若你的项目结构不同则需要显式覆盖:

  • tsConfig:默认 tsconfig.spec.json
  • buildTarget:默认 ::development(即“项目名:构建目标:development 配置”的简写)。

当项目缺少 development 构建配置、或需要与默认不同的测试选项时,你可以新建一个名为 testing(或其它名称)的构建配置,并把 buildTarget 指向它,例如:

{
  "projects": {
    "your-project-name": {
      "architect": {
        "test": {
          "builder": "@angular/build:unit-test",
          "options": {
            "buildTarget": "your-project-name:build:testing"
          }
        }
      }
    }
  }
}

需要特别注意 builder 能力的差异:旧的 @angular/build:karma 允许把构建类选项(如 polyfillsassetsstyles)直接配置在 test target 内部;而新的 @angular/build:unit-test 不再支持这种用法。如果你的测试专用构建选项与既有 development 构建配置不同,就必须把它们迁移到独立的构建 target 配置中;若本来就与 development 配置一致,则无需任何额外动作。

第 3 步:处理自定义 karma.conf.js 配置

karma.conf.js 中的自定义配置不会被自动迁移。在删除该文件前,务必逐项审查其中的自定义设置。

大多数 Karma 选项在 Vitest 中都有对应物,可以写入自定义 Vitest 配置文件(如 vitest.config.ts),再通过 angular.jsonrunnerConfig 选项挂接给 builder。常见迁移路径如下:

Karma 概念 Vitest 对应方案
Reporters(报告器) 替换为 Vitest 兼容的报告器,通常可直接在 angular.jsontest.options.reporters 中配置;更高级的配置使用自定义 vitest.config.ts
Plugins(插件) Karma 插件需要你自行查找并安装对应的 Vitest 等价插件;注意代码覆盖率在 Angular CLI 中是内建一等能力,直接运行 ng test --coverage 即可启用
Custom Browser Launchers(自定义浏览器启动器) angular.json 中的 browsers 选项 + 安装浏览器 provider(如 @vitest/browser-playwright)取代

其余设置请查阅官方 Vitest 配置文档

第 4 步:删除 Karma 与 test.ts 文件

现在可以从项目中删除 karma.conf.jssrc/test.ts,并卸载 Karma 相关包。下面命令以全新 Angular CLI 项目默认安装的包为准,你的项目可能还有其它需要清理的 Karma 相关包:

# npm
npm uninstall karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter jasmine-core

# yarn
yarn remove karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter jasmine-core

# pnpm
pnpm remove karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter jasmine-core

# bun
bun remove karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter jasmine-core

第 5 步(可选):配置浏览器模式(Browser mode)

若确实需要在真实浏览器中运行测试(而不使用 Node.js 内置的 DOM 模拟),需要安装浏览器 provider 并配置 angular.json

安装浏览器 provider,三者选一:

  • Playwright@vitest/browser-playwright,支持 Chromium、Firefox、WebKit;
  • WebdriverIO@vitest/browser-webdriverio,支持 Chrome、Firefox、Safari、Edge;
  • Preview@vitest/browser-preview,面向 WebContainer 环境(如 StackBlitz)。
# 以 Playwright 为例
npm install --save-dev @vitest/browser-playwright

# yarn
yarn add --dev @vitest/browser-playwright

# pnpm
pnpm add -D @vitest/browser-playwright

# bun
bun add --dev @vitest/browser-playwright

更新 angular.json 启用浏览器模式:在 test target 的 options 中加入 browsers 数组。浏览器名称取决于所装的 provider(例如 Playwright 用 chromium,WebdriverIO 用 chrome):

{
  "projects": {
    "your-project-name": {
      "architect": {
        "test": {
          "builder": "@angular/build:unit-test",
          "options": {
            "browsers": ["chromium"]
          }
        }
      }
    }
  }
}

关于有头/无头模式的自动判定:只要设置了 CI 环境变量,或者浏览器名包含 “Headless”(如 ChromeHeadless),就会自动启用无头(headless)模式;否则测试会在有头(headed)浏览器中运行。


三、用 schematic 自动重构测试代码

重要提示:refactor-jasmine-vitest schematic 同样处于实验性阶段,不可能覆盖所有测试模式。schematic 产生的所有改动都需要人工复核。

Angular CLI 提供了 refactor-jasmine-vitest schematic,用于把 Jasmine 测试自动重构为 Vitest 写法。

3.1 它能做什么

该 schematic 会对测试文件(.spec.ts)自动执行以下变换:

  • fit / fdescribeit.only / describe.only
  • xit / xdescribeit.skip / describe.skip
  • spyOn → 等价的 vi.spyOn
  • jasmine.objectContainingexpect.objectContaining
  • jasmine.anyexpect.any
  • jasmine.createSpyvi.fn
  • beforeAllbeforeEachafterAllafterEach → 各自对应的 Vitest 钩子;
  • fail() → Vitest 的 vi.fail()
  • 调整断言(expectations)以匹配 Vitest API;
  • 对无法自动转换的代码添加 TODO 注释。

3.2 它不会做什么

明确哪些事情 schematic 不会代办,有助于判断仍需手工处理的清单:

  • 不会安装 vitest 或其它相关依赖;
  • 不会修改 angular.json 去使用 Vitest builder,也不会polyfillsstyles 等构建选项从 test target 迁移出去(这需要你在第 2 步手工完成);
  • 不会删除 karma.conf.jstest.ts 文件;
  • 不会处理复杂的、嵌套的 spy 场景——这类情况可能需要手工重构。

3.3 如何运行

待项目完成 Vitest 运行器配置(即前文手工迁移步骤)后,即可重构测试文件。若要重构默认项目中的所有测试文件:

ng g @schematics/angular:refactor-jasmine-vitest

3.4 可用选项

选项 说明
--project <name> 在多项目工作区中指定要重构的项目。示例:--project=my-lib
--include <path> 只重构指定文件或目录。示例:--include=src/app/app.component.spec.ts
--file-suffix <suffix> 指定不同的测试文件后缀。示例:--file-suffix=.test.ts
--add-imports 当你在 Vitest 配置中关闭了 globals 时,为测试文件显式添加 vitest 导入
--verbose 查看所应用的全部转换的详细日志
--browser-mode 若你打算在浏览器模式下运行测试,则使用该选项

3.5 迁移完成后的收尾动作

schematic 结束后,建议按以下顺序确认迁移质量:

  1. 运行测试:执行 ng test,确认所有测试在重构后依然通过;
  2. 审查改动:仔细检查 schematic 所做的改动,尤其要关注包含复杂 spy / mock 的测试——它们很可能需要进一步的人工调整。

值得说明的是 ng test 的行为差异:该命令会以 watch 模式构建应用并启动已配置的 runner。当处于交互式终端且非 CI 环境时,watch 模式默认开启。


四、Vitest 的配置机制

Angular CLI 会替你承担大部分 Vitest 配置工作:它会根据 angular.json 中的选项,在内存中构建出完整的 Vitest 配置,并不要求项目根目录出现 vitest.config.ts

4.1 自定义 Vitest 配置(Custom Vitest configuration)

重要提示:使用自定义配置虽然能解锁高级选项,但 Angular 团队不提供对配置文件具体内容的直接支持,也不对其中引用的任何第三方插件负责。为保证正常运行,CLI 还会覆盖若干属性(test.projectstest.include)。

可通过两种方式提供自定义 Vitest 配置文件,覆盖默认设置(完整选项列表见官方 Vitest 配置文档)。

方式一:直接指定路径

{
  "projects": {
    "your-project-name": {
      "architect": {
        "test": {
          "builder": "@angular/build:unit-test",
          "options": {"runnerConfig": "vitest.config.ts"}
        }
      }
    }
  }
}

方式二:自动搜索共享基础配置

runnerConfig 设为 true,builder 会自动在项目根目录与工作区根目录搜索共享的 vitest-base.config.* 文件。


五、zone.js 的 Vitest patch:让 fakeAsync 家族继续可用

如果你的既有测试仍在使用 fakeAsyncflushwaitForAsync 这类基于 zone.js 的测试工具,迁移到 Vitest 后它们并不会自动工作——因为 Vitest runner 默认不会为测试过程建立 Zone 上下文。解决办法是在 angular.json 中把 zone.js/plugins/vitest-patch 加入 test target 的 polyfills

{
  "projects": {
    "your-project-name": {
      "architect": {
        "test": {
          "options": {
            "polyfills": ["zone.js/plugins/vitest-patch"]
          }
        }
      }
    }
  }
}

5.1 patch 的源码实现与原理

这份补丁并非 CLI 黑盒,其实现就存在于本仓库的 zone.js 中,入口为 packages/zone.js/lib/vitest/rollup-vitest.ts(仅做 patchVitest(Zone) 调用),核心逻辑在 packages/zone.js/lib/vitest/vitest.ts

  • 补丁通过 Zone.__load_patch('vitest', ...) 注册,会先检测 Vitest runner 是否注入了全局 vitest 对象,并用 __zone_patch__ 标记防止重复打补丁(vitest.ts);
  • 打补丁前会强制校验 ProxyZoneSpecSyncTestZoneSpec 已就位,并据此 fork 出两条 Zone:SyncTestZoneSpec('vitest.describe') 用于把 describe/suite 的 body 放入仅同步 Zone 执行,ProxyZoneSpec 用于让 it/test 与各类钩子在测试执行期间获得正确的异步代理上下文(vitest.ts);
  • 补丁覆盖了 suite/describeit/test 两组 API 的全部修饰形式:直接修饰符 skiponlyconcurrentsequentialshuffletodo,以及柯里化修饰符 skipIfrunIfeachforvitest.ts),确保 it.onlydescribe.skipit.each 这类写法同样被正确包裹;同时 beforeEachafterEachbeforeAllafterAll 也会用 ProxyZone 包裹执行(vitest.ts);
  • 有一个容易被忽视的细节:补丁在包装测试函数时特意同步了函数的 length 属性,以便 Vitest 核心正确判断测试函数是否声明了 done 参数(vitest.ts)。

在打包侧,vitest-patch 作为 zone.js 的标准 bundle 目标之一被声明于 packages/zone.js/bundles.bzl,其入口正是 vitest/rollup-vitest;对应的回归测试见 packages/zone.js/test/vitest/vitest-patch-globals.spec.js

5.2 关于未来的测试写法

无论如何,官方文档都强烈建议你尽早规划把既有测试套件迁移到原生 async/await 以及 Vitest 内建的 fake timers(mock clock)——这才是长期被推荐的既定路线。

在测试编写层面,你可以参考 guide/testing/components-scenarios.md 中 “Async test with a Vitest fake timers” 一节的现成范例:它展示了在 TestBed 环境下用 vi.useFakeTimers() 启动假定时器、vi.runAllTimersAsync() 推进异步任务、最后 vi.useRealTimers() 恢复真实时钟的完整模式。该节还明确指出:fakeAsync 这类基于 zone.js 打补丁的 mock clock 已不再推荐使用,优先选择原生 async 策略或 Vitest/Jasmine 的 fake timers。


六、迁移后的验证与问题反馈

迁移完成后,除了执行 ng test 验证全部用例通过外,还建议:

  • 关注任何依赖精确计时或真实 DOM 事件的用例——Node.js DOM 模拟环境(jsdom/happy-dom)与真实浏览器的行为存在差异;
  • 留意被 schematic 标注 TODO 的片段,逐一人工补齐。

如果遇到 bug 或有功能诉求,可以前往 Angular CLI 仓库提交 issue(详见官方文档 migrating-to-vitest.md 末尾的反馈指引),提交时尽量附带可最小复现的样例,以便团队更快定位问题。


七、速查:迁移清单总览

# 事项 操作要点
1 安装依赖 vitest + jsdom(或 happy-dom),后者可选
2 换 builder test.builder@angular/build:unit-test;构建类选项移入独立 build target
3 迁移 Karma 配置 检查 karma.conf.js 的 reporters / plugins / launchers,迁移到 Vitest 或 angular.json
4 清理旧文件与包 删除 karma.conf.jssrc/test.ts,卸载 karma / jasmine 系列依赖
5(可选) 浏览器模式 安装 @vitest/browser-* provider 并配置 options.browsers
6 重构测试代码 ng g @schematics/angular:refactor-jasmine-vitest(可选配 project/include/verbose 等)
7 配置与 polyfill 必要时用 runnerConfig 挂自定义 Vitest 配置;用到 fakeAsync 系工具则加 zone.js/plugins/vitest-patch
8 验证 ng test + 逐项审查 schematic 改动,重点检查复杂 spy/mock

整个迁移链路中,本仓库可为你提供两类一手资料:一是权威的官方指南文档(本文所依据的 migrating-to-vitest.md 及其兄弟文档);二是 zone.js 中可直接阅读的 Vitest 补丁源码与测试,帮助你理解迁移后在 Zone 环境下异步测试的真实行为边界。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388