搭配 Mocha 跑测试套件 - describe it 完整示例
为什么常和 Mocha 搭配
Mocha 是一个轻量、灵活的 JavaScript 测试框架,本身不带断言库。它的职责是:发现测试文件、调度 describe / it 块、生成测试报告、支持异步超时等。
Chai + Mocha 是最经典的组合之一——Mocha 提供测试运行能力,Chai 提供断言能力,各司其职。
安装
npm install mocha chai --save-dev
package.json scripts 建议:
{
"scripts": {
"test": "mocha test/**/*.test.js"
}
}
一个完整的测试文件
假设有一个要测试的模块 src/calculator.js:
// src/calculator.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
module.exports = { add, multiply };
对应的测试文件 test/calculator.test.js:
const { expect } = require('chai');
const { add, multiply } = require('../src/calculator');
describe('Calculator', function () {
describe('#add()', function () {
it('1 + 1 应该等于 2', function () {
expect(add(1, 1)).to.equal(2);
});
it('负数相加', function () {
expect(add(-1, -2)).to.equal(-3);
});
});
describe('#multiply()', function () {
it('2 * 3 应该等于 6', function () {
expect(multiply(2, 3)).to.equal(6);
});
});
});
运行 npm test,Mocha 会自动发现文件并执行,输出彩色报告。
describe / it 的组织规则
describe(name, fn)组织一组相关测试,可以无限嵌套。it(name, fn)单个测试用例,name 应该读起来像一个规格说明(如「应该返回正确的结果」)。context(name, fn)是describe的别名,语义上用于表达「在某种条件下」。
describe('银行账户', function () {
context('存款操作', function () {
it('应该增加账户余额', function () { /* ... */ });
it('不能存入负数金额', function () { /* ... */ });
});
context('取款操作', function () {
it('余额不足时应该拒绝', function () { /* ... */ });
});
});
同步断言(最常见)
绝大多数断言都是同步的,it 回调里直接写就行:
it('应该返回排序后的数组', function () {
const input = [3, 1, 2];
const result = sort(input);
expect(result).to.deep.equal([1, 2, 3]);
});
异步断言 - 回调模式
如果被测试函数接受 callback(err, result) 形式的回调:
it('异步获取用户信息', function (done) {
getUserById(123, function (err, user) {
if (err) return done(err);
expect(user.name).to.be.a('string');
expect(user.id).to.equal(123);
done(); // 告诉 Mocha 测试完成
});
});
Mocha 通过检测 it 的参数个数(function(done) 有一个参数 = 异步)来决定是否等待回调。
异步断言 - Promise 模式
如果被测试函数返回 Promise,直接 return 给 Mocha:
it('异步获取用户信息(Promise)', function () {
return getUserById(123).then(function (user) {
expect(user.name).to.be.a('string');
});
});
异步断言 - async/await 模式
ES2017 语法,最清爽:
it('异步获取用户信息(async/await)', async function () {
const user = await getUserById(123);
expect(user.name).to.be.a('string');
expect(user.id).to.equal(123);
});
Mocha 原生支持 async/await。
钩子函数(Hooks)
测试前后的准备和清理工作:
describe('数据库连接', function () {
before(function () {
// 整个 describe 块开始前只跑一次
return db.connect();
});
beforeEach(function () {
// 每个 it 开始前跑一次
return db.clearTables();
});
afterEach(function () {
// 每个 it 结束后跑一次
});
after(function () {
// 整个 describe 块结束后只跑一次
return db.close();
});
});
四种钩子都支持异步(done 回调或 return Promise / async)。
配置文件 .mocharc
Mocha 支持多种配置方式,推荐用 .mocharc.js:
module.exports = {
timeout: 5000, // 单个测试超时时间(毫秒)
reporter: 'spec', // 报告格式:spec / dot / min / nyan / json
require: ['chai/register-should'], // 全局注册 should 风格
files: ['test/**/*.test.js'],
exit: true // 强制退出(处理异步清理)
};
这样就不需要在每个测试文件里手动 require('chai').should() 了。
常用 CLI 选项
# 只跑某个文件
npx mocha test/calculator.test.js
# 只跑包含特定关键词的测试(grep)
npx mocha --grep "1 \+ 1"
# 以 JSON 格式输出结果,方便 CI 解析
npx mocha --reporter json
# 递归扫描子目录
npx mocha --recursive test/
如果用 Jest 呢
Jest 自带 expect,不需要 Chai。但如果你更喜欢 Chai 的风格(比如链式语言),也可以在 Jest 里注册 Chai:
// setupFilesAfterEach 指向的文件
require('chai/register-expect');
不过大多数场景下,Jest + Jest expect 就够用了。选 Jest 还是 Mocha 主要取决于团队偏好和项目已有基础设施。
下一步
跑起来之后,你可能想:
- 系统化了解三种断言风格的差异 → 风格对比
- 处理异步测试 → 异步测试
- HTTP 接口测试 → chai-http 插件