Jasmine 简介 #
什么是 Jasmine? #
Jasmine 是一个行为驱动开发(Behavior-Driven Development,BDD)风格的 JavaScript 测试框架。它提供了简洁自然的语法来编写测试,不依赖任何浏览器、DOM 或其他 JavaScript 框架。
核心定位 #
text
┌─────────────────────────────────────────────────────────────┐
│ Jasmine │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ BDD 语法 │ │ 断言库 │ │ Spy 功能 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 异步支持 │ │ 无依赖 │ │ 跨平台 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
Jasmine 的历史 #
发展历程 #
text
2008年 ─── Jasmine 项目启动
│
│ 由 Pivotal Labs 开发
│ 受 RSpec 和 JSSpec 启发
│
2010年 ─── 开源发布
│
│ GitHub 开源
│ 社区快速发展
│
2012年 ─── Jasmine 1.3
│
│ 稳定版本发布
│ 广泛应用于项目
│
2014年 ─── Jasmine 2.0
│
│ 重写异步支持
│ 改进 Spy 功能
│
2017年 ─── Jasmine 3.0
│
│ 更好的错误消息
│ Promise 支持
│
2020年 ─── Jasmine 4.0
│
│ ES Module 支持
│ 性能优化
│
2023年 ─── Jasmine 5.0
│
│ 现代化改进
│ 更好的 TypeScript 支持
│
至今 ─── 行业标准
│
│ Angular 默认测试框架
│ 数百万项目使用
里程碑版本 #
| 版本 | 时间 | 重要特性 |
|---|---|---|
| 1.0 | 2010 | 开源发布,基础 BDD 语法 |
| 1.3 | 2012 | 稳定版本,广泛采用 |
| 2.0 | 2014 | 异步支持重写,done() 回调 |
| 2.5 | 2016 | 自定义匹配器改进 |
| 3.0 | 2017 | Promise 支持,错误消息改进 |
| 3.5 | 2019 | 更好的异步处理 |
| 4.0 | 2020 | ES Module 支持,性能优化 |
| 5.0 | 2023 | 现代化改进,TypeScript 支持 |
为什么选择 Jasmine? #
传统测试的痛点 #
在没有 Jasmine 之前,JavaScript 测试面临以下问题:
javascript
// 需要配置多个工具
// package.json
{
"devDependencies": {
"mocha": "^9.0.0", // 测试运行器
"chai": "^4.3.0", // 断言库
"sinon": "^12.0.0", // Spy/Mock 库
"karma": "^6.3.0" // 测试运行器
}
}
// 需要编写复杂配置文件
// karma.conf.js, mocha.opts 等
Jasmine 的解决方案 #
javascript
// 一站式解决方案
// package.json
{
"devDependencies": {
"jasmine": "^5.0.0" // 一个包搞定所有
}
}
// 直接开始写测试
// spec/sum.spec.js
describe('Calculator', function() {
it('should add two numbers', function() {
expect(add(1, 2)).toBe(3);
});
});
Jasmine 的核心特点 #
1. BDD 风格语法 #
自然语言般的测试描述:
javascript
describe('Calculator', function() {
describe('add', function() {
it('should add two positive numbers', function() {
expect(add(1, 2)).toBe(3);
});
it('should handle negative numbers', function() {
expect(add(-1, -2)).toBe(-3);
});
});
});
2. 无依赖设计 #
不依赖任何外部库或 DOM:
text
┌─────────────────────────────────────────────────┐
│ Jasmine 核心 │
├─────────────────────────────────────────────────┤
│ ✅ 无 DOM 依赖 │
│ ✅ 无 jQuery 依赖 │
│ ✅ 无 Node.js 特定 API │
│ ✅ 可在任何 JS 环境运行 │
└─────────────────────────────────────────────────┘
3. 内置断言库 #
丰富的匹配器:
javascript
it('demonstrates matchers', function() {
expect(1 + 1).toBe(2);
expect({ a: 1 }).toEqual({ a: 1 });
expect(value).toBeDefined();
expect(value).toBeTruthy();
expect([1, 2, 3]).toContain(2);
expect('hello world').toMatch(/world/);
expect(fn).toThrow();
});
4. 强大的 Spy 功能 #
测试替身功能:
javascript
describe('with spy', function() {
it('tracks calls', function() {
spyOn(obj, 'method');
obj.method('arg');
expect(obj.method).toHaveBeenCalled();
expect(obj.method).toHaveBeenCalledWith('arg');
});
it('returns stubbed value', function() {
spyOn(obj, 'method').and.returnValue(42);
expect(obj.method()).toBe(42);
});
});
5. 异步支持 #
完善的异步测试:
javascript
// 使用 done 回调
it('async test', function(done) {
asyncFunction().then(function(result) {
expect(result).toBe('expected');
done();
});
});
// 使用 async/await
it('async test', async function() {
const result = await asyncFunction();
expect(result).toBe('expected');
});
Jasmine 与其他测试框架对比 #
Jasmine vs Jest #
| 特性 | Jasmine | Jest |
|---|---|---|
| 零配置 | ⚠️ 需初始化 | ✅ 开箱即用 |
| 执行速度 | 快 | 更快 |
| 快照测试 | ❌ 不支持 | ✅ 内置 |
| 代码覆盖率 | ⚠️ 需配置 | ✅ 内置 |
| 并行执行 | ⚠️ 需配置 | ✅ 自动 |
| 学习曲线 | 平缓 | 平缓 |
| Angular 集成 | ✅ 官方默认 | ⚠️ 需配置 |
Jasmine vs Mocha #
| 特性 | Jasmine | Mocha |
|---|---|---|
| 断言库 | ✅ 内置 | ❌ 需要 chai |
| Spy 功能 | ✅ 内置 | ❌ 需要 sinon |
| 配置复杂度 | 简单 | 复杂 |
| 灵活性 | 中等 | 高 |
| 社区生态 | 成熟 | 成熟 |
Jasmine vs Vitest #
| 特性 | Jasmine | Vitest |
|---|---|---|
| ESM 支持 | ✅ 支持 | ✅ 原生支持 |
| 执行速度 | 快 | 更快 |
| Vite 集成 | ❌ 无 | ✅ 原生支持 |
| Jest 兼容 | ⚠️ 部分 | ✅ 高度兼容 |
Jasmine 的应用场景 #
1. 单元测试 #
测试独立的函数或模块:
javascript
describe('Math utilities', function() {
describe('add', function() {
it('should add two numbers', function() {
expect(add(1, 2)).toBe(3);
});
});
describe('multiply', function() {
it('should multiply two numbers', function() {
expect(multiply(2, 3)).toBe(6);
});
});
});
2. Angular 应用测试 #
Angular 官方推荐的测试框架:
typescript
describe('CalculatorComponent', () => {
let component: CalculatorComponent;
let fixture: ComponentFixture<CalculatorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [CalculatorComponent]
}).compileComponents();
fixture = TestBed.createComponent(CalculatorComponent);
component = fixture.componentInstance;
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
3. Node.js 后端测试 #
测试后端逻辑:
javascript
describe('UserService', function() {
let userService;
let mockDb;
beforeEach(function() {
mockDb = jasmine.createSpyObj('Database', ['find', 'save']);
userService = new UserService(mockDb);
});
it('should find user by id', async function() {
mockDb.find.and.returnValue(Promise.resolve({ id: 1, name: 'John' }));
const user = await userService.findById(1);
expect(user.name).toBe('John');
});
});
4. 前端组件测试 #
测试 UI 组件:
javascript
describe('Button component', function() {
let button;
let clickHandler;
beforeEach(function() {
clickHandler = jasmine.createSpy('clickHandler');
button = document.createElement('button');
button.textContent = 'Click me';
button.addEventListener('click', clickHandler);
document.body.appendChild(button);
});
afterEach(function() {
document.body.removeChild(button);
});
it('should handle click', function() {
button.click();
expect(clickHandler).toHaveBeenCalled();
});
});
Jasmine 的核心概念 #
测试套件 (Suite) #
使用 describe 定义测试套件:
javascript
describe('Calculator', function() {
});
测试用例 (Spec) #
使用 it 定义测试用例:
javascript
it('should add two numbers', function() {
expect(add(1, 2)).toBe(3);
});
断言 (Expectation) #
使用 expect 进行断言:
javascript
expect(actual).toBe(expected);
expect(actual).toEqual(expected);
匹配器 (Matcher) #
定义断言规则:
javascript
expect(value).toBe(3);
expect(value).toEqual({ a: 1 });
expect(value).toBeTruthy();
Spy #
监视和控制函数:
javascript
spyOn(obj, 'method');
spyOnProperty(obj, 'property');
jasmine.createSpy('name');
Jasmine 的设计哲学 #
1. 可读性优先 #
javascript
// 自然语言般的测试描述
describe('ShoppingCart', function() {
describe('addItem', function() {
it('should add item to cart', function() {
});
it('should update total price', function() {
});
});
});
2. 约定优于配置 #
text
my-project/
├── spec/
│ ├── support/
│ │ └── jasmine.json # 配置文件
│ ├── helpers/ # 辅助函数
│ └── *.spec.js # 测试文件
└── src/
└── *.js # 源代码
3. 独立性 #
每个测试应该独立运行:
javascript
describe('User', function() {
let user;
beforeEach(function() {
user = new User('John');
});
it('should have correct name', function() {
expect(user.name).toBe('John');
});
it('should update name', function() {
user.setName('Jane');
expect(user.name).toBe('Jane');
});
});
Jasmine 的局限性 #
已知限制 #
- 无快照测试:不支持 UI 快照测试
- 并行执行:默认串行执行测试
- 代码覆盖率:需要额外配置
解决方案 #
javascript
// 使用 karma-coverage 获取覆盖率
// 使用 parallel-spec-runner 插件实现并行
// 对于快照测试,考虑使用 Jest
学习路径 #
text
入门阶段
├── 了解 Jasmine 概念
├── 安装与配置
├── 编写第一个测试
├── 使用断言匹配器
└── 测试生命周期
进阶阶段
├── 异步测试
├── Spy 功能
├── 自定义匹配器
└── 配置选项
高级阶段
├── 高级特性
├── 集成测试
└── 最佳实践
实战阶段
├── Angular 测试
├── Node.js 测试
└── 前端项目测试
下一步 #
现在你已经了解了 Jasmine 的基本概念,接下来学习 基础测试 开始实际使用 Jasmine!
最后更新:2026-03-28