ava、jasmine、jest 和 mocha 都是 JavaScript 生态系统中用于编写和运行自动化测试的核心工具。它们帮助开发者验证代码逻辑、防止回归错误并确保应用稳定性。jest 以“开箱即用”的全能特性著称,集成了运行器、断言库和模拟功能;mocha 则提供极高的灵活性,允许开发者自由组合断言和模拟库;ava 专注于利用 Node.js 的并行能力加速测试执行;而 jasmine 作为老牌行为驱动开发(BDD)框架,常用于传统项目或 Angular 生态中。
在构建可靠的前端或 Node.js 应用时,选择合适的测试框架是架构决策中的关键一步。jest、mocha、ava 和 jasmine 都能帮你编写自动化测试,但它们的设计哲学、执行模型和开发体验截然不同。作为架构师,我们需要透过表面的语法差异,理解它们在工程实践中的真实表现。
测试运行器的核心任务是加载并执行测试文件。不同的框架对并发处理有不同的策略,这直接影响大型项目的测试速度。
jest 默认并行运行测试文件,但在单个文件内串行执行测试用例。它通过沙箱隔离测试环境。
// jest: 文件间并行,文件内串行
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('adds 2 + 2 to equal 4', () => {
expect(sum(2, 2)).toBe(4);
});
mocha 默认串行执行所有测试。它本身不提供并行运行功能,通常需要配合第三方工具(如 mocha-parallel-tests)或集群模式来实现并发。
// mocha: 默认串行执行
describe('Math Suite', function() {
it('adds 1 + 2 to equal 3', function() {
assert.equal(sum(1, 2), 3);
});
it('adds 2 + 2 to equal 4', function() {
assert.equal(sum(2, 2), 4);
});
});
ava 的核心优势是真正的并行执行。它会在单独的进程中运行每个测试文件,甚至可以在文件内并行运行异步测试,极大提升速度。
// ava: 真正的并行执行
import test from 'ava';
test('adds 1 + 2 to equal 3', async t => {
t.is(sum(1, 2), 3);
});
test('adds 2 + 2 to equal 4', async t => {
t.is(sum(2, 2), 4);
});
jasmine 类似 Mocha,默认串行执行。它专注于行为驱动开发(BDD)风格,执行模型较为传统。
// jasmine: 串行执行
describe('Math Suite', function() {
it('adds 1 + 2 to equal 3', function() {
expect(sum(1, 2)).toBe(3);
});
it('adds 2 + 2 to equal 4', function() {
expect(sum(2, 2)).toBe(4);
});
});
断言是测试的核心,用于验证结果是否符合预期。框架是否内置断言库决定了你的依赖复杂度。
jest 内置了强大的 expect 全局对象,支持链式调用和丰富的匹配器。
// jest: 内置 expect
test('object assignment', () => {
const data = { one: 1 };
data['two'] = 2;
expect(data).toEqual({ one: 1, two: 2 });
});
mocha 本身 不包含 断言库。你需要自行安装 chai、should.js 或使用 Node 内置的 assert 模块。
// mocha: 需引入外部断言 (以 Node assert 为例)
const assert = require('assert');
it('object assignment', function() {
const data = { one: 1 };
data['two'] = 2;
assert.deepStrictEqual(data, { one: 1, two: 2 });
});
ava 拥有自己的一套断言 API,通过测试对象 t 传递。它的设计简洁,不支持链式调用,强调明确性。
// ava: 通过 t 对象断言
import test from 'ava';
test('object assignment', t => {
const data = { one: 1 };
data['two'] = 2;
t.deepEqual(data, { one: 1, two: 2 });
});
jasmine 内置了 expect 断言库,风格与 Jest 非常相似(因为 Jest 早期基于 Jasmine)。
// jasmine: 内置 expect
it('object assignment', function() {
const data = { one: 1 };
data['two'] = 2;
expect(data).toEqual({ one: 1, two: 2 });
});
在单元测试中,隔离外部依赖(如 API 调用、数据库)至关重要。框架对 Mock 和 Spy 的支持程度直接影响测试编写的便利性。
jest 提供了一流的内置模拟功能。你可以轻松模拟函数、模块甚至定时器。
// jest: 内置 jest.fn() 和 jest.mock()
const mockFn = jest.fn();
mockFn('a');
expect(mockFn).toHaveBeenCalledWith('a');
jest.mock('axios');
mocha 没有内置模拟功能。社区标准做法是搭配 sinon 库使用。
// mocha: 通常搭配 sinon
const sinon = require('sinon');
it('calls callback', function() {
const callback = sinon.spy();
process(callback);
sinon.assert.calledOnce(callback);
});
ava 同样不内置模拟功能,推荐结合 sinon 或 testdouble 使用。这保持了核心库的轻量。
// ava: 搭配 sinon
import test from 'ava';
import sinon from 'sinon';
test('calls callback', t => {
const callback = sinon.spy();
process(callback);
t.true(callback.calledOnce);
});
jasmine 内置了 spies 功能,无需额外库即可监控函数调用。
// jasmine: 内置 spyOn
it('calls callback', function() {
const callback = jasmine.createSpy('callback');
process(callback);
expect(callback).toHaveBeenCalled();
});
现代测试框架通常提供快照测试(Snapshot Testing)来防止 UI 或数据结构意外变更。
jest 以“零配置”闻名,内置快照测试功能。只需一个命令即可生成和比对快照。
// jest: 内置快照
test('snapshot', () => {
const component = renderComponent();
expect(component).toMatchSnapshot();
});
mocha 不支持快照测试。如果需要此功能,必须引入第三方插件(如 jest-snapshot 的独立版或 chai-jest-snapshot)。
// mocha: 需引入插件支持快照
// 无原生代码示例,需配置 chai-jest-snapshot 等插件
ava 内置了快照测试功能,用法与 Jest 类似,但存储格式和命令略有不同。
// ava: 内置快照
import test from 'ava';
test('snapshot', t => {
t.snapshot({ title: 'Hello' });
});
jasmine 原生不支持快照测试。通常需要借助 jasmine-snapshots 等社区插件来实现。
// jasmine: 需引入插件
// 无原生代码示例,依赖社区扩展
尽管实现方式不同,这四个框架在核心目标上是一致的。它们都支持异步测试、提供钩子函数(Hooks)来管理生命周期,并且都能集成到 CI/CD 流程中。
所有框架都支持 Promise 和 async/await,这是现代 JavaScript 测试的标配。
// jest
test('fetches data', async () => {
const data = await fetchData();
expect(data).toBeDefined();
});
// mocha
it('fetches data', async function() {
const data = await fetchData();
assert.ok(data);
});
// ava
test('fetches data', async t => {
const data = await fetchData();
t.truthy(data);
});
// jasmine
it('fetches data', async function() {
const data = await fetchData();
expect(data).toBeDefined();
});
它们都提供了 beforeEach、afterEach 等钩子,用于在测试前后清理状态或设置环境。
// 所有框架语法类似
beforeEach(() => {
// 重置数据库或状态
resetDatabase();
});
都支持只运行特定测试(如 test.only 或 it.only),方便调试单个用例。
// jest/mocha/jasmine
test.only('debug this', () => { ... });
// ava
test.only('debug this', t => { ... });
| 特性 | jest | mocha | ava | jasmine |
|---|---|---|---|---|
| 执行方式 | 并行 (文件级) | 串行 (默认) | 并行 (进程级) | 串行 (默认) |
| 断言库 | ✅ 内置 (expect) | ❌ 需外部 (如 Chai) | ✅ 内置 (t.is) | ✅ 内置 (expect) |
| 模拟功能 | ✅ 内置 (jest.fn) | ❌ 需外部 (如 Sinon) | ❌ 需外部 (如 Sinon) | ✅ 内置 (spyOn) |
| 快照测试 | ✅ 内置 | ❌ 需插件 | ✅ 内置 | ❌ 需插件 |
| 配置复杂度 | 低 (零配置) | 高 (自由组合) | 中 (约定优于配置) | 低 (简单配置) |
| 主要优势 | 功能全面,DX 好 | 灵活,可定制 | 速度快,隔离好 | 经典,依赖少 |
jest 就像一把瑞士军刀 🇨🇭 —— 它几乎包含了你需要的所有功能。对于大多数 React、Vue 或 Node.js 新项目,它是默认的最佳选择。它的生态最丰富,遇到问题最容易找到解决方案。
mocha 更像是一个精密的工具箱 🧰 —— 它只提供核心运行器,其余工具由你挑选。适合那些对构建流程有严格控制,或者已经积累了大量基于 Chai/Sinon 测试代码的企业级项目。
ava 是一台高性能引擎 🏎️ —— 如果你的测试套件运行时间过长,或者你担心测试之间的状态污染,Ava 的并行和隔离模型能带来显著改善。它适合对性能敏感的大型代码库。
jasmine 是一位经验丰富的老兵 🎖️ —— 它在 Angular 旧版本项目中非常常见。除非你正在维护这类项目,否则在新项目中选择 Jest 通常会获得更好的长期支持。
最终建议:对于 90% 的现代前端项目,jest 是最稳妥的起点。只有当你有明确的性能瓶颈(选 ava)或特殊的架构约束(选 mocha)时,才考虑其他选项。
选择 jest 如果你希望拥有一个零配置、功能全面的测试解决方案。它内置了断言、模拟、代码覆盖率和快照测试,非常适合 React 项目或需要快速搭建测试环境的新项目。
选择 mocha 如果你需要高度定制化的测试架构,或者正在维护一个已经基于 Chai 和 Sinon 构建测试体系的老项目。它不强制绑定任何断言库,适合对测试流程有精细控制需求的团队。
选择 jasmine 如果你正在维护一个传统的 Angular 项目,或者偏好经典的 BDD 风格且不需要复杂的构建配置。它依赖较少,但在现代前端生态中的新功能支持上不如 Jest 活跃。
选择 ava 如果你的测试套件非常庞大且运行缓慢,需要利用并行执行来节省时间。它强制测试隔离,避免状态污染,适合对测试稳定性和执行效率有极高要求的场景。
🃏 Delightful JavaScript Testing
👩🏻💻 Developer Ready: Complete and ready to set-up JavaScript testing solution. Works out of the box for any React project.
🏃🏽 Instant Feedback: Failed tests run first. Fast interactive mode can switch between running all tests or only test files related to changed files.
📸 Snapshot Testing: Jest can capture snapshots of React trees or other serializable values to simplify UI testing.
Read More: https://jestjs.io/