Chai 中文文档 下载 App

搭配 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('银行账户', 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 主要取决于团队偏好和项目已有基础设施。

下一步

跑起来之后,你可能想: