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 的局限性 #

已知限制 #

  1. 无快照测试:不支持 UI 快照测试
  2. 并行执行:默认串行执行测试
  3. 代码覆盖率:需要额外配置

解决方案 #

javascript
// 使用 karma-coverage 获取覆盖率
// 使用 parallel-spec-runner 插件实现并行
// 对于快照测试,考虑使用 Jest

学习路径 #

text
入门阶段
├── 了解 Jasmine 概念
├── 安装与配置
├── 编写第一个测试
├── 使用断言匹配器
└── 测试生命周期

进阶阶段
├── 异步测试
├── Spy 功能
├── 自定义匹配器
└── 配置选项

高级阶段
├── 高级特性
├── 集成测试
└── 最佳实践

实战阶段
├── Angular 测试
├── Node.js 测试
└── 前端项目测试

下一步 #

现在你已经了解了 Jasmine 的基本概念,接下来学习 基础测试 开始实际使用 Jasmine!

最后更新:2026-03-28