Angular 项目使用 Karma 与 Jasmine 进行单元测试:配置、覆盖率门槛、CI 运行与浏览器调试完整指南
Karma 是一款被广泛使用的测试运行器(Test Runner),配合 Jasmine 测试框架,曾长期是 Angular CLI 默认的单元测试技术栈。随着 Vitest 成为新项目的默认测试运行器,Karma 依旧是受官方支持、存量项目中非常普及的选项。本文基于 Angular 官方文档中的《Testing with Karma and Jasmine》测试指南,结合本仓库内的真实配置与源码证据,系统讲解如何在 Angular 应用中从零配置 Karma + Jasmine、运行与调试测试、落地覆盖率门槛,并在 CI 中以无头浏览器稳定执行测试。读完本文,你将掌握一套可直接复制到实际项目中的完整 Karma 单元测试工作流。
在开始前先明确一个概念边界:Karma 是测试运行器(负责启动浏览器、加载被测代码与测试文件、收集并汇报结果),Jasmine 是测试框架(提供 describe、it、expect 等断言与用例组织语法)。两者分工不同,日常口中所说的“Karma 测试”实际上通常是这套"Karma 运行器 + Jasmine 框架"组合。
Karma + Jasmine 在当前 Angular 技术栈中的定位
从本仓库维护的文档与路线图可以看到清晰的演进脉络:
- 新项目的默认单元测试运行器已是 Vitest,相关说明见 testing/overview.md 与 reference/roadmap.md,其中明确指出 Vitest 已成为 Angular 的主要测试运行器,团队正在推动将“Karma 迁移至 Vitest”的实验性工具走向稳定;
- 与此同时,Karma 仍然是一款受支持且被广泛使用的运行器,大量存量项目依旧依赖 Karma + Jasmine;
- 本仓库的文档站应用
adev自身的test目标就是一个活生生的真实案例——它没有使用 Vitest,而是在新的统一@angular/build:unit-testbuilder 下显式指定runner: "karma"。
查看 adev/angular.json 可看到该目标的完整写法:
"test": {
"builder": "@angular/build:unit-test",
"options": {
"runner": "karma",
"browsers": ["ChromeHeadlessNoSandbox"],
"include": ["src/app/**/*.spec.ts"]
}
}
这个例子恰好验证了本文要讲解的两点核心知识:Karma 已被收编到统一的 unit-test builder 下,通过 runner 选项切换运行器;同时测试文件约定使用 *.spec.ts 命名并通过 include 圈定。
搭建 Karma 与 Jasmine
Karma + Jasmine 既可以随新项目一键初始化,也可以手动添加到既有项目中。
新建项目:一条命令完成预配置
使用 Angular CLI 的 ng new 时,通过 --test-runner=karma 选项即可生成带好 Karma 与 Jasmine 完整配置的项目:
ng new my-karma-app --test-runner=karma
在 adev/src/content/cli/new.json 中对 test-runner 选项的官方描述即为"The unit testing runner to use",说明该选项正是用来在脚手架阶段选定单元测试运行器。
既有项目:三步手动接入
如果项目已经存在,可按以下三个步骤把 Karma + Jasmine 补进来。
步骤 1:安装依赖包
根据你使用的包管理器选择对应命令:
npm install --save-dev karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter jasmine-core @types/jasmine yarn add --dev karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter jasmine-core @types/jasmine pnpm add -D karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter jasmine-core @types/jasmine bun add --dev karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter jasmine-core @types/jasmine各包职责如下:
karma:测试运行器本体;karma-jasmine:Karma 与 Jasmine 框架的适配插件;karma-chrome-launcher:负责启动 Chrome / ChromeHeadless 浏览器执行测试的启动器;karma-coverage:基于 Istanbul 的覆盖率采集插件(本仓库 adev 侧使用的 karma 模板中引用的正是它);karma-jasmine-html-reporter:在浏览器中以可视化的 Jasmine Spec Runner 呈现测试结果;jasmine-core:Jasmine 框架本体;@types/jasmine:为 TypeScript 提供describe、it、expect等全局 API 的类型声明。
步骤 2:在 angular.json 中把测试运行器切换为 Karma
找到 architect.test 目标,确保 builder 为 @angular/build:unit-test,并在 options 中设置 runner: "karma":
{
// ...
"projects": {
"your-project-name": {
// ...
"architect": {
"test": {
"builder": "@angular/build:unit-test",
"options": {
"runner": "karma"
// ... other options
}
}
}
}
}
}
若你的项目仍在使用旧的 @angular/build:karma 专用 builder,可参考迁移说明进行调整(详见 migrating-to-vitest.md 中对新旧 builder 差异的说明:旧 builder 允许在 test 目标内直接配置 polyfills、assets、styles 等构建选项,而统一的 unit-test builder 不再支持,这些选项需收敛到 build 目标,测试默认复用 development 构建配置,tsConfig 默认指向 tsconfig.spec.json)。
步骤 3:在 tsconfig.spec.json 中加入 Jasmine 类型
为了让 TypeScript 在编写测试时识别全局测试函数 describe、it 等,需要在 tsconfig.spec.json 的 compilerOptions.types 中显式加入 "jasmine":
{
// ...
"compilerOptions": {
// ...
"types": ["jasmine"]
}
}
这里用 types 数组显式收窄全局类型来源,能避免把 @types/* 下所有包自动注入全局作用域,是 Angular 官方推荐的规范写法。
运行测试
配置完成后,即可通过 ng test 命令执行测试:
ng test
ng test 会以 watch(监听)模式构建应用,并启动 Karma 测试运行器。构建完成后,控制台输出与下面类似:
02 11 2022 09:08:28.605:INFO [karma-server]: Karma v6.4.1 server started at http://localhost:9876/
02 11 2022 09:08:28.607:INFO [launcher]: Launching browsers Chrome with concurrency unlimited
02 11 2022 09:08:28.620:INFO [launcher]: Starting browser Chrome
02 11 2022 09:08:31.312:INFO [Chrome]: Connected on socket -LaEYvD2R7MdcS0-AAAB with id 31534482
Chrome: Executed 3 of 3 SUCCESS (0.193 secs / 0.172 secs)
TOTAL: 3 SUCCESS
解读这段日志的关键信息:
- Karma 服务默认监听
http://localhost:9876/,浏览器与该服务建立 socket 连接后开始执行用例; - 逐行阅读可见 Karma 的完整生命周期:
karma-server启动 →launcher以"并发数不限"策略启动 Chrome → Chrome 连接成功 → 汇总 "Executed 3 of 3 SUCCESS" → 输出TOTAL。
Karma 会额外在浏览器窗口中展示交互式测试结果(由 karma-jasmine-html-reporter 驱动,见仓库 karma.conf.js 模板中的 jasmineHtmlReporter 配置):
- 点击某一条测试用例所在行,可只重新运行该用例;
- 点击某个 describe 描述(测试分组/测试套件),可只重跑该分组内的所有用例。
与此同时 ng test 一直在监听文件变化:当你改动任意源文件并保存后,测试会立刻重新执行,浏览器自动刷新并展示最新结果——这正是 watch 模式下"改代码 → 跑测试 → 看结果"的高频反馈循环。
一个典型的 Karma + Jasmine 用例由 describe(分组)、it(单条用例)与 expect(断言)构成,组件级测试还会用到 TestBed.createComponent() 创建组件夹具,例如(来自 components-basics.md 的最小用例):
describe('Banner (minimal)', () => {
it('should create', () => {
const fixture = TestBed.createComponent(Banner);
const component = fixture.componentInstance;
expect(component).toBeDefined();
});
});
深度配置:理解 karma.conf.js
Angular CLI 会替你打理 Jasmine 与 Karma 的大部分配置:它基于 angular.json 中的选项,在内存中构造完整配置,无需显式配置文件即可运行。但当默认行为不满足需求时,可以生成一份自定义配置。
生成 karma.conf.js
在项目根目录执行:
ng generate config karma
即可生成 karma.conf.js,Angular CLI 之后会加载并合并其中自定义的内容。
karma.conf.js 关键字段逐项解读
本仓库维护的 karma.conf.js 模板 是官方新项目脚手架的完整写照,逐段拆解如下:
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
jasmine: {
// 可在此添加 Jasmine 配置
// 例如 random: false 关闭随机执行顺序,seed: 4321 固定随机种子
},
clearContext: false, // 让 Jasmine Spec Runner 输出常驻浏览器,便于反复查看
},
jasmineHtmlReporter: {
suppressAll: true, // 移除重复的失败堆栈输出
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/example-app'),
subdir: '.',
reporters: [{type: 'html'}, {type: 'text-summary'}],
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true,
});
};
字段含义与影响:
| 字段 | 含义与建议值 |
|---|---|
basePath |
所有相对路径解析的根目录,默认空字符串即配置文件所在目录 |
frameworks |
声明使用的框架插件;固定为 ['jasmine', '@angular-devkit/build-angular'],后者负责把 Angular 编译产物桥接给 Karma |
plugins |
显式列出加载的 Karma 插件;若项目使用旧版,模板中该处可能为 karma-coverage-istanbul-reporter(见本仓库 integration/cli-hello-world/karma.conf.js),新版已迁移至 karma-coverage |
client.jasmine |
向 Jasmine 透传的配置:可用 random: false 关闭随机用例执行顺序,或用 seed: 4321 固定随机种子以复现特定顺序下的失败 |
client.clearContext |
设为 false 时保留 Spec Runner 输出在浏览器中,便于失败后查看 |
jasmineHtmlReporter.suppressAll |
抑制 HTML Reporter 中重复的输出轨迹 |
coverageReporter |
覆盖率产物目录与报告类型,常见为 html + text-summary |
reporters |
控制台与浏览器报告器,['progress', 'kjhtml'] 为默认组合 |
port |
Karma 服务端口,默认 9876 |
autoWatch |
是否监听文件变化自动重跑,开发期应为 true |
browsers |
执行测试的浏览器,开发用 Chrome,CI 用 ChromeHeadless |
singleRun |
是否为单次运行;开发 watch 期为 false,CI 应配合 --no-watch 语义一次跑完退出 |
restartOnFileChange |
文件变化时重启整个浏览器实例(比增量重跑更干净) |
仓库中的集成测试项目还展示了为 CI 定制无头浏览器启动器的做法(integration/cli-hello-world/karma.conf.js),在容器/CI 环境下用 ChromeHeadless 加参数绕开沙箱限制:
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
flags: [
'--no-sandbox',
'--headless',
'--disable-gpu',
'--disable-dev-shm-usage',
'--hide-scrollbars',
'--mute-audio',
],
},
},
browsers: ['ChromeHeadlessNoSandbox'],
这一点恰好呼应了本文前面引用的 adev/angular.json:官方文档站自身的测试目标在浏览器选择上同样使用了 ChromeHeadlessNoSandbox。
通过 angular.json 与 runner-config 精细控制
从 cli/test.json 中 ng test 命令的选项定义可以归纳出,unit-test builder 在 Karma 场景下还提供以下常用选项:
runner:指定测试运行器,取karma或vitest;runner-config:指定所选运行器的配置文件路径;传字符串作为配置文件路径,传true时自动搜索默认配置文件(例如karma.conf.js),传false时不加载任何外部配置文件;code-coverage:开启覆盖率报告;未指定时会回退使用运行器配置文件中的覆盖率配置;browsers/include等:控制执行浏览器与纳入测试的*.spec.ts文件范围。
需要特别留意 --headless 这一选项在 cli/test.json 中有明确说明:Karma 运行器不支持该选项——无头模式应通过把 browsers 配置为 ChromeHeadless(或自定义 ChromeHeadlessNoSandbox)来实现,而非传 --headless 标志。
覆盖率门槛强制校验
要让"测试跑完并通过"不足以代表质量,可以设置最低代码覆盖率:未达标即让本次测试失败。在 karma.conf.js 的 coverageReporter 段使用 check 属性即可实现。
例如,要求全局语句、分支、函数、行四个维度覆盖率均不低于 80%:
coverageReporter: {
dir: require('path').join(__dirname, './coverage/<project-name>'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
],
check: {
global: {
statements: 80,
branches: 80,
functions: 80,
lines: 80
}
}
}
要点说明:
dir使用require('path').join(__dirname, ...)拼出基于项目根目录的绝对输出路径,<project-name>需替换为实际项目名;subdir: '.'让各类报告直接输出到coverage/<project-name>,避免默认按浏览器维度再套一层子目录;reporters中的html用于生成可交互的网页报告,text-summary用于在控制台输出汇总行;check.global定义全局最低阈值,四个指标可分别设置;一旦实际覆盖率低于阈值,Karma 会让测试运行以失败结束。除此之外还可针对每个文件(each)单独设限,适合对关键模块施加强约束。
开启覆盖率统计可在运行测试时追加 ng test --code-coverage。关于覆盖率工作流的更多实操细节(包括如何开启与查看报告),可继续阅读 code-coverage.md。
在持续集成(CI)中运行 Karma
在 CI 环境中没有图形界面、也不需要持续监听,应使用一次性无头运行:
ng test --no-watch --no-progress --browsers=ChromeHeadless
三个标志各有不可替代的作用:
--no-watch:跑完一轮即退出,避免进程因 watch 永不结束导致 CI 任务挂起;--no-progress:关闭逐条进度输出,避免 CI 日志被刷屏、也减少终端缓冲负担;--browsers=ChromeHeadless:在无图形界面的 CI 机器上以 Chrome 无头模式执行。若 CI 运行在容器中(例如 Jenkins/Docker),往往还需要配合前文介绍的ChromeHeadlessNoSandbox启动器(--no-sandbox等 flags),这正是仓库 integration 各项目 karma.conf.js 中封装自定义 launcher 的动机。
在浏览器中调试失败用例
测试行为不符合预期时,可以直接在浏览器里单步调试。由于 Karma 用例运行在真实浏览器中,调试体验与调试应用代码一致。
调试步骤:
- 找到 Karma 打开的浏览器窗口(若它未自动弹出,可先参考测试环境搭建说明,见 testing/overview.md,确保测试已能正常跑起来);
- 点击浏览器页面上的 DEBUG 按钮,Karma 会打开一个新标签页并重新执行测试;
- 打开浏览器的开发者工具(Windows 按
Ctrl-Shift-I,macOS 按Command-Option-I); - 切换到 Sources 面板;
- 按
Control/Command-P,输入测试文件名快速打开对应的 spec 文件; - 在测试代码中打上断点;
- 刷新浏览器,即可看到执行停在你设置的断点上,随后可逐行观察变量与调用栈。
下图示意了打断点后的调试现场——可看到测试停在了 spec 中的断言位置,右侧为作用域与调用栈信息:
补充两点实用技巧:由于 Karma 默认会缓存编译产物,调试改完代码后记得刷新浏览器标签让最新版本生效;若想聚焦调试某条用例,可先在 Jasmine Spec Runner 页面点击对应用例行单独重跑,减少噪音。更系统的调试指引还可参考 debugging.md。
更多相关资源
如果你是在维护一个 Karma + Jasmine 的存量项目、想评估后续演进方向,以下仓库内文档可以衔接阅读:
- 组件测试基础:
TestBed、ComponentFixture、fakeAsync等组件测试核心 API 的完整讲解; - 代码覆盖率指南:覆盖率开启、报告与门槛设置的进阶说明;
- 从 Karma 与 Jasmine 迁移到 Vitest:当存量项目需要切换到 Vitest 时,介绍自动化重构 schematic、
angular.json改造与运行器配置差异,可作为本文的自然延伸。
小结:Karma + Jasmine 仍是 Angular 生态中成熟、稳定且受官方支持的单元测试组合。本文从新项目脚手架与存量项目迁移两个入口梳理了完整接入流程,并结合本仓库真实的 angular.json、karma.conf.js 与 CLI 选项定义,详解了 runner、runner-config、覆盖率门槛、CI 无头执行与浏览器调试等核心实践。掌握这套工作流后,你可以在任何 Angular 项目中快速落地可观测、可量化、可持续的单元测试基础设施。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00

