Mocha 是一个功能丰富的 JavaScript 测试框架,可在 Node.js 和浏览器中运行,使异步测试变得简单且有趣。 Mocha 测试按顺序运行,允许灵活且准确的报告,同时将未捕获的异常映射到正确的测试用例。 托管在 GitHub 上。
# 赞助商
在工作中使用 Mocha?询问您的经理或营销团队他们是否愿意帮助 支持 我们的项目。 您公司的徽标也将显示在 npmjs.com 和我们的 GitHub 存储库 上。
# 支持者
发现 Mocha 有用?成为 支持者 并通过每月捐款支持 Mocha。
# 特性
- 浏览器支持
- 简单的异步支持,包括 Promise
- 并行运行 Node.js 测试
- 测试覆盖率报告
- 字符串差异支持
- 用于运行测试的 JavaScript API
- 自动检测并为非 TTY 禁用颜色
- 异步测试超时支持
- 测试重试支持
- 特定于测试的超时
- 报告测试持续时间
- 突出显示缓慢的测试
- 文件观察者支持
- 全局变量泄漏检测
- 可以选择运行与正则表达式匹配的测试
- 自动退出以防止在活动循环中“挂起”
- 轻松元生成套件 和 测试用例
- 配置文件支持
- 节点调试器支持
- 节点原生 ES 模块支持
- 源映射支持
- 检测对
done()
的多次调用 - 使用您想要的任何断言库
- 可扩展的报告,捆绑了 9 个以上的报告器
- 可扩展的测试 DSL 或“接口”
- before、after、beforeEach、afterEach 钩子
- 任意转译器支持(coffee-script 等)
- TextMate 捆绑包
# 目录
- 安装
- 入门
- 运行周期概述
- 检测对
done()
的多次调用 - 断言
- 异步代码
- 同步代码
- 箭头函数
- 钩子
- 待处理测试
- 排他性测试
- 包含性测试
- 重试测试
- 动态生成测试
- 超时
- 差异
- 命令行用法
- 并行测试
- 根钩子插件
- 全局夹具
- 测试夹具决策树向导
- 接口
- 报告器
- Node.JS 原生 ESM 支持
- 在浏览器中运行 Mocha
- 配置 Mocha (Node.js)
test/
目录- 错误代码
- 编辑器插件
- 示例
- 测试 Mocha
- 更多信息
# 安装
使用 npm 全局安装
$ npm install --global mocha
或作为项目的开发依赖项
$ npm install --save-dev mocha
从 v10.0.0 开始,Mocha 需要 Node.js v14.0.0 或更高版本。
# 入门
$ npm install mocha
$ mkdir test
$ $EDITOR test/test.js # or open with your favorite editor
在您的编辑器中
var assert = require('assert');
describe('Array', function () {
describe('#indexOf()', function () {
it('should return -1 when the value is not present', function () {
assert.equal([1, 2, 3].indexOf(4), -1);
});
});
});
回到终端
$ ./node_modules/mocha/bin/mocha.js
Array
#indexOf()
✓ should return -1 when the value is not present
1 passing (9ms)
在 package.json 中设置测试脚本
"scripts": {
"test": "mocha"
}
然后使用以下命令运行测试
$ npm test
# 运行周期概述
针对 v8.0.0 更新。
以下是 Mocha 在 Node.js 中运行时的“执行流程”的中级概述;“不太重要”的细节已被省略。
在浏览器中,测试文件由 <script>
标签加载,调用 mocha.run()
从第 9 步开始 下面。
# 串行模式
- 用户(就是您)执行
mocha
- 加载来自配置文件的选项(如果存在)
- Mocha 处理提供的任何命令行选项(有关详细信息,请参阅有关 配置合并 的部分)
- 如果找到
node
可执行文件的已知标志- Mocha 将在子进程中生成
node
,并使用这些标志执行自身 - 否则,Mocha 不会生成子进程
- Mocha 将在子进程中生成
- Mocha 加载由
--require
指定的模块- 如果以这种方式加载的文件包含已知的 Mocha 特定导出(例如,根钩子插件),Mocha 将“注册”这些导出
- 如果不是,Mocha 将忽略
--require
’d 模块的任何导出
- Mocha 验证通过
--require
或其他方式加载的任何自定义报告器或接口 - Mocha 发现测试文件;当没有给出文件或目录时,它会找到当前工作目录相对路径的
test
目录(但不包括其子目录)中的扩展名为.js
、.mjs
或.cjs
的文件 - (默认)bdd 接口 以无特定顺序加载测试文件,这些文件被赋予特定于接口的
global
上下文(这就是例如describe()
最终成为测试文件中的全局变量的方式)- 当加载测试文件时,Mocha 会执行其所有套件并找到——但不会执行——其中的任何钩子和测试。
- 顶级钩子、测试和套件都是“不可见”根套件的成员;整个进程只有一个根套件
- Mocha 运行 全局设置夹具(如果有)
- 从“根”套件开始,Mocha 执行
- 任何“beforeAll”钩子(对于根套件,这只会发生一次;请参阅 根钩子插件)
- 对于每个测试,Mocha 执行
- 任何“beforeEach”钩子
- 测试(并报告结果)
- 任何“afterEach”钩子
- 如果当前套件有子套件,请对每个子套件重复步骤 10;每个子套件继承其父级中定义的任何“beforeEach”和“afterEach”钩子
- 任何“afterAll”钩子(对于根套件,这只会发生一次;请参阅 根钩子插件)
- Mocha 打印最终摘要/结语(如果适用)
- Mocha 运行 全局拆卸夹具(如果有)
# 并行模式
- 重复步骤 1 到 6,来自上面的 串行模式,跳过报告器验证
- 找到的所有测试文件都被放入队列中(它们不会被主进程加载)
- Mocha 运行 全局设置夹具(如果有)
- Mocha 创建一个子进程池(“工作者”)
- 在工作者运行它接收的第一个测试之前,工作者通过以下方式“引导”自身
- 加载所有
--require
’d 模块 - 注册任何根钩子插件
- 忽略全局夹具和自定义报告器
- 断言内置或自定义接口有效
- 加载所有
- 当工作者收到要运行的测试文件时,工作者会为单个测试文件创建一个新的 Mocha 实例,并且
- 工作者重复步骤 8,来自上面的 步骤
- 工作者重复步骤 10,来自上面的 步骤,但工作者不会直接报告测试结果;它将它们保存在内存缓冲区中
- 当工作者完成测试文件时,缓冲的结果将返回到主进程,主进程然后将它们提供给用户指定的报告器(默认情况下为
spec
) - 工作者使自身可用于池;如果还有剩余的测试文件,池会将另一个测试文件提供给工作者运行
- Mocha 打印最终摘要/结语(如果适用)
- Mocha 运行 全局拆卸夹具(如果有)
# 检测对 done()
的多次调用
如果您使用基于回调的异步测试,如果多次调用 done()
,Mocha 将抛出错误。 这对于捕获意外的双重回调非常有用。
it('double done', function (done) {
// Calling `done()` twice is an error
setImmediate(done);
setImmediate(done);
});
运行上面的测试将给出以下错误消息
$ ./node_modules/.bin/mocha mocha.test.js
✓ double done
1) double done
1 passing (6ms)
1 failing
1) double done:
Error: done() called multiple times
at Object.<anonymous> (mocha.test.js:1:63)
at require (internal/module.js:11:18)
at Array.forEach (<anonymous>)
at startup (bootstrap_node.js:187:16)
at bootstrap_node.js:608:3
# 断言
Mocha 允许您使用任何您想要的断言库。 在上面的示例中,我们使用的是 Node.js 的内置 assert 模块——但通常,如果它抛出 Error
,它将起作用! 这意味着您可以使用以下库
- should.js - 这些文档中展示的 BDD 样式
- expect.js -
expect()
样式断言 - chai -
expect()
、assert()
和should
样式断言 - better-assert - C 样式自文档化
assert()
- unexpected - “可扩展的 BDD 断言工具包”
# 异步代码
通过向 it()
的测试回调添加一个参数(通常命名为 done
),Mocha 将知道它应该等待此函数被调用以完成测试。 此回调接受 Error
实例(或其子类)或假值;任何其他内容都是无效用法,会抛出错误(通常会导致测试失败)。
describe('User', function () {
describe('#save()', function () {
it('should save without error', function (done) {
var user = new User('Luna');
user.save(function (err) {
if (err) done(err);
else done();
});
});
});
});
或者,直接使用 done()
回调(它将处理错误参数,如果存在)
describe('User', function () {
describe('#save()', function () {
it('should save without error', function (done) {
var user = new User('Luna');
user.save(done);
});
});
});
# 使用 Promise
或者,您可以返回一个 Promise,而不是使用 done()
回调。 如果您正在测试的 API 返回 Promise 而不是接受回调,这很有用
beforeEach(function () {
return db.clear().then(function () {
return db.save([tobi, loki, jane]);
});
});
describe('#find()', function () {
it('respond with matching records', function () {
return db.find({type: 'User'}).should.eventually.have.length(3);
});
});
后面的示例使用 Chai as Promised 进行流畅的 Promise 断言。
在 Mocha v3.0.0 及更高版本中,返回 Promise
和调用 done()
将导致异常,因为这通常是一个错误
const assert = require('assert');
// antipattern
it('should complete this test', function (done) {
return new Promise(function (resolve) {
assert.ok(true);
resolve();
}).then(done);
});
上面的测试将失败,显示 Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
。 在 v3.0.0 之前的版本中,对 done()
的调用实际上被忽略了。
# 使用 async / await
如果您的 JS 环境支持 async / await,您也可以像这样编写异步测试
beforeEach(async function () {
await db.clear();
await db.save([tobi, loki, jane]);
});
describe('#find()', function () {
it('responds with matching records', async function () {
const users = await db.find({type: 'User'});
users.should.have.length(3);
});
});
# 同步代码
在测试同步代码时,省略回调,Mocha 将自动继续进行下一个测试。
describe('Array', function () {
describe('#indexOf()', function () {
it('should return -1 when the value is not present', function () {
[1, 2, 3].indexOf(5).should.equal(-1);
[1, 2, 3].indexOf(0).should.equal(-1);
});
});
});
# 箭头函数
将 箭头函数(也称为“lambda”)传递给 Mocha 是不鼓励的。 Lambda 按词法绑定 this
,并且无法访问 Mocha 上下文。 例如,以下代码将失败
describe('my suite', () => {
it('my test', () => {
// should set the timeout of this test to 1000 ms; instead will fail
this.timeout(1000);
assert.ok(true);
});
});
如果您不需要使用 Mocha 的上下文,lambda 应该可以工作。 请注意,如果最终需要,使用 lambda 将更难重构!
# 钩子
使用其默认的“BDD”样式接口,Mocha 提供了 before()
、after()
、beforeEach()
和 afterEach()
钩子。 这些应该用于设置先决条件并在测试后清理。
describe('hooks', function () {
before(function () {
// runs once before the first test in this block
});
after(function () {
// runs once after the last test in this block
});
beforeEach(function () {
// runs before each test in this block
});
afterEach(function () {
// runs after each test in this block
});
// test cases
});
测试可以出现在您的钩子之前、之后或与您的钩子交织在一起。 钩子将按定义的顺序运行,具体取决于情况;所有
before()
钩子都会运行(一次),然后是任何beforeEach()
钩子、测试、任何afterEach()
钩子,最后是after()
钩子(一次)。
# 描述钩子
任何钩子都可以使用可选的描述调用,这使得更容易查明测试中的错误。 如果钩子被赋予一个命名函数,则如果没有提供描述,将使用该名称。
beforeEach(function () {
// beforeEach hook
});
beforeEach(function namedFun() {
// beforeEach:namedFun
});
beforeEach('some description', function () {
// beforeEach:some description
});
# 异步钩子
所有钩子(before()
、after()
、beforeEach()
、afterEach()
)都可以是同步或异步的,行为与常规测试用例非常相似。 例如,您可能希望在每次测试之前使用虚拟内容填充数据库
describe('Connection', function () {
var db = new Connection(),
tobi = new User('tobi'),
loki = new User('loki'),
jane = new User('jane');
beforeEach(function (done) {
db.clear(function (err) {
if (err) return done(err);
db.save([tobi, loki, jane], done);
});
});
describe('#find()', function () {
it('respond with matching records', function (done) {
db.find({type: 'User'}, function (err, res) {
if (err) return done(err);
res.should.have.length(3);
done();
});
});
});
});
# 根级别钩子
在测试文件的最顶层范围(在套件之外)定义的钩子是根钩子。
从 v8.0.0 开始,根钩子插件 是设置根钩子的首选机制。
# 延迟根套件
警告:延迟根套件与 并行模式 不兼容。
如果您需要在运行任何套件之前执行异步操作(例如,用于动态生成测试),您可以延迟根套件。 使用 --delay
标志运行 mocha
。 这将附加一个特殊的回调函数 run()
到全局上下文
const assert = require('assert');
const fn = async x => {
return new Promise(resolve => {
setTimeout(resolve, 3000, 2 * x);
});
};
// instead of an IIFE, you can use 'setImmediate' or 'nextTick' or 'setTimeout'
(async function () {
const z = await fn(3);
describe('my suite', function () {
it(`expected value ${z}`, function () {
assert.strictEqual(z, 6);
});
});
run();
})();
# 待处理测试
“待定” — 就像“有人应该最终编写这些测试用例” — 待定测试用例是指那些*没有*回调函数的测试用例。
describe('Array', function () {
describe('#indexOf()', function () {
// pending test below
it('should return -1 when the value is not present');
});
});
待定测试将包含在测试结果中,并标记为待定。待定测试不被视为失败测试。
阅读包含测试部分,了解如何使用this.skip()
有条件地将测试标记为待定的示例。
# 排他性测试
警告:排他性测试与并行模式不兼容。
排他性功能允许您通过在函数末尾添加.only()
来*仅*运行指定的套件或测试用例。以下是如何仅执行特定套件的示例
describe('Array', function () {
describe.only('#indexOf()', function () {
// ...
});
});
注意:所有嵌套套件仍将执行。
以下是如何执行单个测试用例的示例
describe('Array', function () {
describe('#indexOf()', function () {
it.only('should return -1 unless present', function () {
// ...
});
it('should return the index when present', function () {
// ...
});
});
});
在 v3.0.0 之前,.only()
使用字符串匹配来决定要执行哪些测试;现在不再是这种情况。在 v3.0.0 或更高版本中,.only()
可以使用多次来定义要运行的测试子集
describe('Array', function () {
describe('#indexOf()', function () {
it.only('should return -1 unless present', function () {
// this test will be run
});
it.only('should return the index when present', function () {
// this test will also be run
});
it('should return -1 if called with a non-Array context', function () {
// this test will not be run
});
});
});
您也可以选择多个套件
describe('Array', function () {
describe.only('#indexOf()', function () {
it('should return -1 unless present', function () {
// this test will be run
});
it('should return the index when present', function () {
// this test will also be run
});
});
describe.only('#concat()', function () {
it('should return a new Array', function () {
// this test will also be run
});
});
describe('#slice()', function () {
it('should return a new Array', function () {
// this test will not be run
});
});
});
但测试将具有优先级
describe('Array', function () {
describe.only('#indexOf()', function () {
it.only('should return -1 unless present', function () {
// this test will be run
});
it('should return the index when present', function () {
// this test will not be run
});
});
});
注意:如果存在钩子,它们仍将执行。
请注意,不要将
.only()
的用法提交到版本控制中,除非您真的想这样做!为此,可以在持续集成测试命令(或在 git precommit 钩子中)中使用--forbid-only
选项运行 mocha。
# 包含测试
此功能是.only()
的反面。通过添加.skip()
,您可以告诉 Mocha 忽略测试用例。任何被跳过的内容都将被标记为待定,并作为此类内容报告。以下是如何跳过单个测试的示例
describe('Array', function () {
describe('#indexOf()', function () {
it.skip('should return -1 unless present', function () {
// this test will not be run
});
it('should return the index when present', function () {
// this test will be run
});
});
});
您也可以将.skip()
放置在整个套件上。这等同于将.skip()
添加到套件中的所有测试。套件中的钩子也会被跳过。
describe('Array', function () {
describe.skip('#indexOf()', function () {
it('should return -1 unless present', function () {
// this test will not be run
});
});
});
注意:跳过套件中放置在钩子或测试之外的代码仍然会执行,因为 mocha 仍然会调用套件函数来构建套件结构以进行可视化。
最佳实践:使用
.skip()
而不是注释掉测试。
您也可以使用this.skip()
在运行时跳过测试。如果测试需要无法事先检测到的环境或配置,则运行时跳过是合适的。例如
it('should only test in the correct environment', function() {
if (/* check test environment */) {
// make assertions
} else {
this.skip();
}
});
上面的测试将被报告为待定。同样重要的是要注意,调用this.skip()
将有效地中止测试。
最佳实践:为了避免混淆,在调用
this.skip()
后,不要在测试或钩子中执行进一步的指令。
将上面的测试与以下代码进行对比
it('should only test in the correct environment', function() {
if (/* check test environment */) {
// make assertions
} else {
// do nothing
}
});
因为此测试什么也不做,所以它将被报告为通过。
最佳实践:不要什么也不做!测试应该进行断言或使用
this.skip()
。
要以这种方式跳过多个测试,请在“before all”钩子中使用this.skip()
before(function() {
if (/* check test environment */) {
// setup code
} else {
this.skip();
}
});
这将跳过套件中的所有it
、beforeEach/afterEach
和describe
块。before/after
钩子将被跳过,除非它们是在与包含this.skip()
的钩子相同的级别上定义的。
describe('outer', function () {
before(function () {
this.skip();
});
after(function () {
// will be executed
});
describe('inner', function () {
before(function () {
// will be skipped
});
after(function () {
// will be skipped
});
});
});
在 v7.0.0 中更新:在“after all”钩子中跳过测试是不允许的,并且会抛出异常。使用 return 语句或其他方法来中止钩子执行。
在 Mocha v3.0.0 之前,this.skip()
在异步测试和钩子中不受支持。
# 重试测试
您可以选择将失败的测试重试最多一定次数。此功能旨在处理端到端测试(功能测试/Selenium…),其中资源不容易被模拟/存根。不建议将此功能用于单元测试。
此功能确实会重新运行失败的测试及其相应的beforeEach/afterEach
钩子,但不会重新运行before/after
钩子。this.retries()
对失败的钩子没有影响。
注意:以下示例使用 Selenium webdriver(它覆盖了全局 Mocha 钩子,用于Promise
链)。
describe('retries', function () {
// Retry all tests in this suite up to 4 times
this.retries(4);
beforeEach(function () {
browser.get('http://www.yahoo.com');
});
it('should succeed on the 3rd try', function () {
// Specify this test to only retry up to 2 times
this.retries(2);
expect($('.foo').isDisplayed()).to.eventually.be.true;
});
});
# 动态生成测试
鉴于 Mocha 使用函数表达式来定义套件和测试用例,因此动态生成测试非常简单。不需要特殊语法——可以使用普通的 JavaScript 来实现类似于“参数化”测试的功能,您可能在其他框架中见过这种功能。
以下是一个示例
const assert = require('assert');
function add(args) {
return args.reduce((prev, curr) => prev + curr, 0);
}
describe('add()', function () {
const tests = [
{args: [1, 2], expected: 3},
{args: [1, 2, 3], expected: 6},
{args: [1, 2, 3, 4], expected: 10}
];
tests.forEach(({args, expected}) => {
it(`correctly adds ${args.length} args`, function () {
const res = add(args);
assert.strictEqual(res, expected);
});
});
});
上面的代码将生成一个包含三个规范的套件
$ mocha
add()
✓ correctly adds 2 args
✓ correctly adds 3 args
✓ correctly adds 4 args
在.forEach
处理程序内添加的测试通常与编辑器插件配合不好,尤其是“右键单击运行”功能。参数化测试的另一种方法是使用闭包生成它们。以下示例等同于上面的示例
describe('add()', function () {
const testAdd = ({args, expected}) =>
function () {
const res = add(args);
assert.strictEqual(res, expected);
};
it('correctly adds 2 args', testAdd({args: [1, 2], expected: 3}));
it('correctly adds 3 args', testAdd({args: [1, 2, 3], expected: 6}));
it('correctly adds 4 args', testAdd({args: [1, 2, 3, 4], expected: 10}));
});
使用top-level await
,您可以在测试文件加载时以动态和异步的方式收集测试数据。
另请参阅--delay
,了解没有top-level await
的 CommonJS 模块。
// testfile.mjs
import assert from 'assert';
// top-level await: Node >= v14.8.0 with ESM test file
const tests = await new Promise(resolve => {
setTimeout(resolve, 5000, [
{args: [1, 2], expected: 3},
{args: [1, 2, 3], expected: 6},
{args: [1, 2, 3, 4], expected: 10}
]);
});
// in suites ASYNCHRONOUS callbacks are NOT supported
describe('add()', function () {
tests.forEach(({args, expected}) => {
it(`correctly adds ${args.length} args`, function () {
const res = args.reduce((sum, curr) => sum + curr, 0);
assert.strictEqual(res, expected);
});
});
});
测试持续时间
许多报告器将显示测试持续时间并标记运行缓慢的测试(默认:75 毫秒),如 SPEC 报告器所示
测试持续时间有三个级别(如以下图像所示)
- 快速:在“慢”阈值的一半内运行的测试将以绿色显示持续时间(如果有的话)。
- 正常:超过阈值一半(但仍在阈值内)运行的测试将以黄色显示持续时间。
- 慢速:超过阈值运行的测试将以红色显示持续时间。
要调整被视为“慢速”的内容,可以使用slow()
方法
describe('something slow', function () {
this.slow(300000); // five minutes
it('should take long enough for me to go make a sandwich', function () {
// ...
});
});
# 超时
# 套件级别
套件级别超时可以应用于整个测试“套件”,也可以通过this.timeout(0)
禁用。这将被所有嵌套套件和未覆盖值的测试用例继承。
describe('a suite of tests', function () {
this.timeout(500);
it('should take less than 500ms', function (done) {
setTimeout(done, 300);
});
it('should take less than 500ms as well', function (done) {
setTimeout(done, 250);
});
});
# 测试级别
也可以应用特定于测试的超时,或者使用this.timeout(0)
完全禁用超时
it('should take less than 500ms', function (done) {
this.timeout(500);
setTimeout(done, 300);
});
# 钩子级别
也可以应用钩子级别超时
describe('a suite of tests', function () {
beforeEach(function (done) {
this.timeout(3000); // A very long environment setup.
setTimeout(done, 2500);
});
});
同样,使用this.timeout(0)
禁用钩子的超时。
在 v3.0.0 或更高版本中,传递给
this.timeout()
的参数大于最大延迟值 将导致超时被禁用。在 v8.0.0 或更高版本中,this.enableTimeouts()
已被删除。警告:对于异步测试,如果您通过this.timeout(0)
禁用超时,然后没有调用done()
,您的测试将静默退出。
# 差异
Mocha 支持断言库中任何抛出的AssertionError
的err.expected
和err.actual
属性。Mocha 将尝试显示预期结果与断言实际看到的之间的差异。以下是如何使用--inline-diffs
显示“字符串”差异的示例
# 命令行用法
mocha [spec..]
Run tests with Mocha
Commands
mocha inspect [spec..] Run tests with Mocha [default]
mocha init <path> create a client-side Mocha setup at <path>
Rules & Behavior
--allow-uncaught Allow uncaught errors to propagate [boolean]
-A, --async-only Require all tests to use a callback (async) or
return a Promise [boolean]
-b, --bail Abort ("bail") after first test failure [boolean]
--check-leaks Check for global variable leaks [boolean]
--delay Delay initial execution of root suite [boolean]
--dry-run Report tests without executing them [boolean]
--exit Force Mocha to quit after tests complete [boolean]
--fail-zero Fail test run if no test(s) encountered [boolean]
--forbid-only Fail if exclusive test(s) encountered [boolean]
--forbid-pending Fail if pending test(s) encountered [boolean]
--global, --globals List of allowed global variables [array]
-j, --jobs Number of concurrent jobs for --parallel; use 1 to
run in serial
[number] [default: (number of CPU cores - 1)]
-p, --parallel Run tests in parallel [boolean]
--retries Retry failed tests this many times [number]
-s, --slow Specify "slow" test threshold (in milliseconds)
[string] [default: 75]
-t, --timeout, --timeouts Specify test timeout threshold (in milliseconds)
[string] [default: 2000]
-u, --ui Specify user interface [string] [default: "bdd"]
Reporting & Output
-c, --color, --colors Force-enable color output [boolean]
--diff Show diff on failure
[boolean] [default: true]
--full-trace Display full stack traces [boolean]
--inline-diffs Display actual/expected differences
inline within each string [boolean]
-R, --reporter Specify reporter to use
[string] [default: "spec"]
-O, --reporter-option, Reporter-specific options
--reporter-options (<k=v,[k1=v1,..]>) [array]
Configuration
--config Path to config file [string] [default: (nearest rc file)]
-n, --node-option Node or V8 option (no leading "--") [array]
--package Path to package.json for config [string]
File Handling
--extension File extension(s) to load
[array] [default: ["js","cjs","mjs"]]
--file Specify file(s) to be loaded prior to root suite
execution [array] [default: (none)]
--ignore, --exclude Ignore file(s) or glob pattern(s)
[array] [default: (none)]
--recursive Look for tests in subdirectories [boolean]
-r, --require Require module [array] [default: (none)]
-S, --sort Sort test files [boolean]
-w, --watch Watch files in the current working directory for
changes [boolean]
--watch-files List of paths or globs to watch [array]
--watch-ignore List of paths or globs to exclude from watching
[array] [default: ["node_modules",".git"]]
Test Filters
-f, --fgrep Only run tests containing this string [string]
-g, --grep Only run tests matching this string or regexp [string]
-i, --invert Inverts --grep and --fgrep matches [boolean]
Positional Arguments
spec One or more files, directories, or globs to test
[array] [default: ["test"]]
Other Options
-h, --help Show usage information & exit [boolean]
-V, --version Show version number & exit [boolean]
--list-interfaces List built-in user interfaces & exit [boolean]
--list-reporters List built-in reporters & exit [boolean]
Mocha Resources
Chat: https://discord.gg/KeDn2uXhER
GitHub: https://github.com/mochajs/mocha.git
Docs: https://mocha.node.org.cn/
# --allow-uncaught
默认情况下,Mocha 将尝试捕获从运行测试中抛出的未捕获异常,并将这些异常报告为测试失败。使用--allow-uncaught
禁用此行为,并允许未捕获异常传播。通常会导致进程崩溃。
此标志在调试难以跟踪的异常时很有用。
# --async-only, -A
强制执行一项规则,即测试必须以“异步”方式编写,这意味着每个测试都提供一个done
回调或返回一个Promise
。不符合要求的测试将被标记为失败。
# --bail, -b
导致 Mocha 在遇到第一个测试失败后停止运行测试。相应的“after each”和“after all”钩子将执行以进行潜在的清理。
--bail
不意味着--exit
。
# --check-leaks
使用此选项让 Mocha 检查在运行测试时泄漏的全局变量。通过--global
选项指定可接受的全局变量(例如:--check-leaks --global jQuery --global MyLib
)。
# --compilers
--compilers
在 v6.0.0 中被删除。请参阅进一步的解释和解决方法。
# --dry-run
v9.0.0 中新增 报告测试,但不执行任何测试,包括测试和钩子。
# --exit
在 v4.0.0 中更新。
TL;DR:如果您的测试在升级到 Mocha v4.0.0 或更高版本后挂起,请使用--exit
进行快速(但不一定推荐)修复。
在 v4.0.0 版本之前,默认情况下,Mocha 会在完成执行所有测试后强制其自身进程退出。此行为会导致一组潜在问题;它表明测试(或夹具、工具、被测代码等)没有正确清理自身。最终,“脏”测试会导致误报或漏报结果。
“挂起”最常出现在服务器仍在监听端口或套接字仍然打开等情况下。它也可能是像失控的setInterval()
甚至是一个永远未完成的错误Promise
。
v4.0.0(及更高版本)中的默认行为是--no-exit
,而以前是--exit
。
解决此问题的最简单方法是将--exit
传递给 Mocha 进程。这可能很耗时,因为问题并不总是很明显——但这样做是推荐的。
为了确保您的测试没有留下混乱,以下是一些入门想法
- 请参阅Node.js 调试指南
- 使用新的
async_hooks
API (示例) - 尝试使用类似于wtfnode 的工具
- 使用
.only
直到找到导致 Mocha 挂起的测试
# --fail-zero
v9.1.0 中新增 如果没有遇到测试,则使用
exit-code: 1
失败测试运行。
# --forbid-only
强制执行一项规则,即测试不能是排他性的(例如,不允许使用describe.only()
或it.only()
)。
--forbid-only
导致 Mocha 在遇到排他性(“only’d”)测试或套件时失败,并且它将中止进一步的测试执行。
# --forbid-pending
强制执行一项规则,即测试不能被跳过(例如,不允许使用describe.skip()
、it.skip()
或this.skip()
)。
--forbid-pending
导致 Mocha 在遇到跳过(“待定”)测试或套件时失败,并且它将中止进一步的测试执行。
# --global <variable-name>
在 v6.0.0 中更新;该选项是
--global
,而--globals
现在是别名。
定义一个全局变量名。例如,假设您的应用程序故意公开名为app
和YUI
的全局变量,您可能想要添加--global app --global YUI
。
--global
接受通配符。您可以执行--global '*bar'
,它将匹配foobar
、barbar
等。您也可以传递'*'
来忽略所有全局变量。
--global
可以接受逗号分隔的列表;--global app,YUI
等同于--global app --global YUI
。
通过将此选项与--check-leaks
结合使用,您可以指定您期望泄漏到全局范围的已知全局变量的白名单。
# --retries <n>
将失败的测试重试n
次。
Mocha 默认情况下不会重试测试失败。
# --slow <ms>, -s <ms>
以毫秒为单位指定“慢速”测试阈值。Mocha 使用它来突出显示运行时间过长的测试用例。“慢速”测试不被视为失败。
注意:在默认的 spec
报告器中,执行时间为“慢”时间一半的测试将以黄色突出显示;执行时间为整个“慢”时间的测试将以红色突出显示。
# --timeout <ms>, -t <ms>
v6.0.0 中更新:使用 inspect 标志调用 Mocha 时,
--timeout 0
将被隐式使用。不再需要--timeout 99999999
。
指定测试用例超时时间,默认为两秒(2000 毫秒)。超过此时间的测试将被标记为失败。
要覆盖,您可以以毫秒为单位传递超时时间,或使用 s
后缀的值,例如,--timeout 2s
和 --timeout 2000
等效。
要禁用超时,请使用 --timeout 0
。
注意:同步(阻塞)测试也受超时时间限制,但它们不会在代码停止阻塞之前完成。无限循环仍然是无限循环!
# --ui <name>, -u <name>
--ui
选项允许您指定要使用的界面,默认为 bdd
。
# --color, -c, --colors
v6.0.0 中更新。
--colors
现在是--color
的别名。
“强制”启用彩色输出,或者通过 --no-color
强制禁用它。默认情况下,Mocha 使用 supports-color 模块来决定。
在某些情况下,某些以机器可读格式输出的报告器会明确抑制彩色输出。
# --diff
如果可能,在遇到断言失败时显示预期值和实际值之间的差异。
此标志不同寻常,因为它默认值为 true
;使用 --no-diff
来抑制 Mocha 自己的 diff 输出。
一些断言库将提供自己的 diff,在这种情况下,无论默认值如何,都不会使用 Mocha 的 diff。
Mocha 自己的 diff 输出不符合任何已知标准,旨在供人类阅读。
v9.2.1 中新增
默认情况下,字符串在生成 diff 之前会被截断为 8192 个字符。这是为了防止大型字符串出现性能问题。
但是,在比较大型字符串时,这可能会使输出更难解释。因此,可以使用 --reporter-option maxDiffSize=[number]
配置此值。
值为 0 表示没有限制,默认值为 8192 个字符。
# --full-trace
启用“完整”堆栈跟踪。默认情况下,Mocha 尝试将堆栈跟踪提炼成更少噪音(但仍然有用)的输出。
此标志在调试 Mocha 或 Node.js 本身中疑似问题时很有用。
# --inline-diffs
启用“内联”diff,这是用于比较字符串的另一种输出方式。
在处理大型字符串时很有用。
如果断言库提供自己的 diff 输出,则不会执行任何操作。
# --reporter <name>, -R <name>
指定将使用的报告器,默认为 spec
。
允许使用第三方报告器。例如,mocha-lcov-reporter 可以在安装后使用 --reporter mocha-lcov-reporter
使用。
# --reporter-option <option>, -O <option>, --reporter-options <option>
v6.0.0 中更新。可以多次指定。
--reporter-options
现在是--reporter-option
的别名。
以 <key>=<value>
格式提供特定于报告器的选项,例如,--reporter tap --reporter-option tapVersion=13
。
并非所有报告器都接受选项。
可以指定为逗号分隔的列表。
# --config <path>
v6.0.0 中新增
指定 配置文件 的显式路径。
默认情况下,如果未指定 --config
,Mocha 将搜索配置文件;使用 --no-config
来抑制此行为。
# --node-option <name>, -n <name>
v9.1.0 中新增
用于 Node.js 和 V8 选项。Mocha 通过生成新的子进程将这些选项转发给 Node.js。
选项在设置时不带前导破折号 --
,例如 -n require=foo -n unhandled-rejections=strict
也可以指定为逗号分隔的列表:-n require=foo,unhandled-rejections=strict
# --opts <path>
v8.0.0 中移除。请改用 配置文件。
# --package <path>
v6.0.0 中新增
指定 package.json
文件 的显式路径(可能包含 mocha
属性中的配置)。
默认情况下,Mocha 会在当前工作目录或最近的祖先目录中查找 package.json
,并将使用找到的第一个文件(无论它是否包含 mocha
属性);要抑制 package.json
查找,请使用 --no-package
。
# --extension <ext>
具有此扩展名的文件将被视为测试文件。默认为 js
。
指定 --extension
将删除 .js
作为测试文件扩展名;使用 --extension js
重新添加它。例如,要加载 .mjs
和 .js
测试文件,您必须提供 --extension mjs --extension js
。
该选项可以多次给出。该选项接受逗号分隔的列表:--extension a,b
等效于 --extension a --extension b
。
v8.2.0 中新增
--extension
现在支持多部分扩展名(例如,spec.js
)、前导点 (.js
) 以及它们的组合 (.spec.js
);
# --file <file|directory|glob>
警告:
--file
与 并行模式 不兼容。
明确包含要在加载其他测试文件之前加载的测试文件。--file
的多次使用是允许的,并将按给定的顺序加载。
如果您想声明(例如)在所有其他测试文件中的每个测试之前运行的钩子,这将很有用。
以这种方式指定的文件不受 --sort
或 --recursive
的影响。
以这种方式指定的文件应包含一个或多个套件、测试或钩子。如果不是这种情况,请考虑使用 --require
。
# --ignore <file|directory|glob>, --exclude <file|directory|glob>,
明确忽略一个或多个测试文件、目录或 glob(例如,some/**/files*
),否则这些文件将被加载。
可以多次指定。
v10.0.0 中新增:在 Windows 中始终使用正斜杠
/
作为路径分隔符。
使用 --file
指定的文件不受此选项的影响。
# --recursive
在查找测试文件时,递归进入子目录。
请参阅 --extension
以定义哪些文件被视为测试文件。
# --require <module>, -r <module>
在加载用户界面或测试文件之前,需要一个模块。这对以下情况很有用
- 测试工具
- 增强内置函数或全局范围的断言库(例如 should.js)
- 编译器,例如通过 @babel/register 的 Babel 或通过 ts-node 的 TypeScript(使用
--require ts-node/register
)。请参阅 Babel 或 TypeScript 工作示例。
以这种方式需要的模块预计会同步执行;Mocha 不会等待所需模块中的异步任务完成。
您不能使用 --require
来设置钩子。如果您想设置钩子以运行(例如)在每个测试之前运行,请使用 根钩子插件。
从 v8.0.0 开始,Mocha 支持 NodeJS 原生 ESM 的
--require
。没有单独的--import
标志。
# --sort, -S
警告:
--sort
与 并行模式 不兼容。
使用 Array.prototype.sort 对测试文件(按绝对路径)进行排序。
# --watch, -w
在文件更改时重新运行测试。
--watch-files
和 --watch-ignore
选项可用于控制监视哪些文件以进行更改。
可以通过键入 ⓡ ⓢ ⏎ 手动重新运行测试(与 nodemon
的快捷键相同)。
# --watch-files <file|directory|glob>
v7.0.0 中新增
设置 --watch
时要监视的路径或 glob 列表。如果与给定 glob 匹配的文件发生更改或被添加或删除,mocha 将重新运行所有测试。
如果路径是目录,则将监视所有文件和子目录。
默认情况下,将监视当前目录中所有具有 --extension
提供的扩展名且未包含在 node_modules
或 .git
文件夹中的文件。
该选项可以多次给出。该选项接受逗号分隔的列表:--watch-files a,b
等效于 --watch-files a --watch-files b
# --watch-ignore <file|directory|glob>
v7.0.0 中新增
要从监视中排除的路径或 glob 列表。默认为 node_modules
和 .git
。
要排除目录中的所有文件,最好使用 foo/bar
而不是 foo/bar/**/*
。后者仍然会监视目录 foo/bar
,但会忽略对该目录内容的所有更改。
该选项可以多次给出。该选项接受逗号分隔的列表:--watch-ignore a,b
等效于 --watch-ignore a --watch-ignore b
# --fgrep <string>, -f <string>
v6.0.0 中的重大更改;现在与
--grep
相互排斥。
使 Mocha 仅运行标题包含给定 string
的测试。
与 --grep
相互排斥。
# --grep <regexp>, -g <regexp>
v6.0.0 中的重大更改;现在与
--fgrep
相互排斥。
使 Mocha 仅运行与给定 regexp
匹配的测试,该测试在内部被编译为 RegExp。
例如,假设您有“api”相关的测试以及“app”相关的测试,如以下代码段所示;可以使用 --grep api
或 --grep app
来运行其中一个。对于套件或测试用例标题的任何其他部分也是如此,--grep users
也是有效的,甚至 --grep GET
也是有效的。
另一个带双引号的选项:--grep "groupA|groupB"
。
对于更复杂的条件:--grep "/get/i"
。一些 shell(例如 Git-Bash-for-Windows)可能需要:--grep "'/get/i'"
。使用除 ignoreCase /i
(尤其是 /g
和 /y
)之外的标志可能会导致不可预测的结果。
describe('api', function () {
describe('GET /api/users groupA', function () {
it('respond with an array of users', function () {
// ...
});
});
});
describe('app', function () {
describe('GET /users groupB', function () {
it('respond with an array of users', function () {
// ...
});
});
});
与 --fgrep
相互排斥。
# --invert
使用 --grep
或 fgrep
指定的匹配的反向。
需要 --grep
或 --fgrep
(但不能同时使用)。
# --inspect, --inspect-brk, inspect
启用 Node.js 的检查器。
使用 --inspect
/ --inspect-brk
启动 V8 检查器以与 Chrome 开发者工具一起使用。
使用 inspect
启动 Node.js 的内部调试器。
所有这些选项都是相互排斥的。
暗示 --timeout 0
。
# --parallel, -p
v.8.0.0 中新增。
使用 --parallel
标志在工作池中运行测试。
每个测试文件将被放入队列中,并在工作程序可用时执行。
注意:--parallel
对 Mocha 的行为有一些影响,您必须注意。阅读有关 并行运行测试 的更多信息。
# --jobs <count>, -j <count>
v.8.0.0 中新增。
使用 --jobs <count>
指定工作池中最大进程数。
默认值为CPU 核心数减 1。
提示:使用 --jobs 0
或 --jobs 1
临时禁用 --parallel
。
除非与 --parallel
一起使用,否则无效。
# 关于选项类型
在 v6.0.0 中更新。
Mocha 的 --help
输出中,每个标注为 [boolean]
类型的标志都可以通过在标志名称前添加 --no-
来取反。例如,--no-color
将禁用 Mocha 的颜色输出,默认情况下颜色输出是启用的。
除非另有说明,否则所有布尔标志默认值为 false
。
# 关于 node
标志
mocha
可执行文件支持 node
可执行文件支持的所有适用标志。
这些标志根据您的 Node.js 版本而有所不同。
node
标志可以在 Mocha 的 配置 中定义。
v9.1.0 中新增 您还可以使用
--node-option
将node
标志传递给 Node.js。
# --enable-source-maps
Node.js v12.12.0 中新增
如果将 --enable-source-maps
标志传递给 mocha,则会收集源映射并使用它们为转译后的代码提供准确的堆栈跟踪
Error: cool
at Object.<anonymous> (/Users/fake-user/bigco/nodejs-tasks/build/src/index.js:27:7)
-> /Users/fake-user/bigco/nodejs-tasks/src/index.ts:24:7
# 关于 V8 标志
在 node --v8-options
的输出中列出的任何标志(不包括 --v8-options
本身)前添加 --v8-
以使用它。
V8 标志可以在 Mocha 的 配置 中定义。
v9.1.0 中新增 您还可以使用
--node-option
将 V8 标志(不带--v8-
)传递给 Node.js。
# 并行测试
v.8.0.0 中新增。
根据测试的数量和性质,您可能会发现以并行方式运行测试(使用 --parallel
标志)时会获得显著的性能提升。
对于许多用例,并行测试应该开箱即用。但是,您必须了解行为的一些重要含义。
注意:基于 Mocha 构建的第三方库的作者应该阅读本文!
# 报告器限制
由于以下报告器的性质,它们在以并行方式运行测试时无法工作
这些报告器期望 Mocha 在执行之前知道它计划运行多少个测试。此信息在并行模式下不可用,因为测试文件仅在即将运行时加载。
在串行模式下,测试结果将在它们发生时“流式传输”。在并行模式下,报告器输出被缓冲;报告将在每个文件完成后发生。实际上,报告器输出将以“块”形式出现(但否则将相同)。如果测试文件特别慢,则在运行时可能会出现明显的暂停。
# 排他性测试不允许
您不能在并行模式下使用 it.only
、describe.only
、this.only()
等。 这是因为与上面提到的不兼容的报告器相同的原因:在并行模式下,Mocha 不会在运行测试之前将所有文件和套件加载到内存中。
建议的解决方法
提示:如果您的配置文件中定义了并行模式,则可以通过使用
--no-parallel
标志或减少作业数(例如,--jobs=0
)来在命令行中临时禁用它。
# 文件顺序是非确定性的
在并行模式下,Mocha 不保证测试文件运行的顺序,也不保证哪个工作进程运行它们。
因此,以下依赖于顺序的选项不能在并行模式下使用
# 测试持续时间可变性
以并行模式运行测试自然会使用更多系统资源。操作系统可能需要额外的时间来调度和完成某些操作,具体取决于系统负载。因此,可能需要增加单个测试的超时时间,无论是 全局 还是 其他。
# “Bail” 是“尽力而为”
当与 --bail
(或 this.bail()
)一起使用以在第一次失败后退出时,其他测试很可能在同一时间运行。Mocha 必须在退出之前关闭其工作进程。
同样,子进程可能会抛出未捕获的异常。当与 --allow-uncaught
一起使用时,Mocha 会将此异常“冒泡”到主进程,但仍然必须关闭其进程。
无论哪种方式,Mocha 都将“很快”中止测试运行。
# 根钩子不是全局的
注意:这仅适用于在并行模式下运行时。
根钩子是测试文件中未定义在套件内的钩子。使用 bdd
接口的示例
// test/setup.js
// root hook to run before every test (even in other files)
beforeEach(function () {
doMySetup();
});
// root hook to run after every test (even in other files)
afterEach(function () {
doMyTeardown();
});
当通过以下命令运行(在默认的“串行”模式下)时
mocha --file "./test/setup.js" "./test/**/*.spec.js"
setup.js
将首先执行,并为 ./test/**/*.spec.js
中找到的每个测试安装上面显示的两个钩子。
上面的示例在并行模式下不起作用。
当 Mocha 在并行模式下运行时,测试文件不共享相同的进程, 也不共享相同的 Mocha 实例。因此,在测试文件A 中定义的假设根钩子将不存在于测试文件B 中。
以下是一些建议的解决方法
- 在每个测试文件的顶部使用
require('./setup.js')
或import './setup.js'
。最适合那些不喜欢样板代码的人。 - 推荐:使用新的(也是从 v8.0.0 开始的)根钩子插件 系统,在“必需”文件中定义根钩子。
如果您需要只运行一次代码,请改用 全局夹具。
# 不支持浏览器
目前,并行模式仅在 Node.js 中可用。
# 第三方报告器的报告器 API 限制
第三方报告器在尝试访问 Test
、Suite
和 Hook
对象中不存在的属性时可能会遇到问题。如果第三方报告器在并行模式下不起作用(但在串行模式下起作用),请 提交问题。
# 并行模式故障排除
如果您发现您的测试在使用 --parallel
运行时无法正常工作,要么耸耸肩继续前进,要么使用此方便的清单来使事情正常工作
- ✅ 确保您使用的是 支持的报告器。
- ✅ 确保您没有使用 其他不支持的标志。
- ✅ 仔细检查您的 配置文件;配置文件中设置的选项将与任何命令行选项合并。
- ✅ 查找测试中的根钩子(它们看起来像 这样)。将它们移到 根钩子插件 中。
- ✅ 您使用的任何断言、模拟或其他测试库是否使用根钩子?它们可能需要 迁移 以与并行模式兼容。
- ✅ 如果测试意外超时,您可能需要增加默认测试超时时间(通过
--timeout
) - ✅ 确保您的测试不依赖于以特定顺序运行。
- ✅ 确保您的测试在自身之后清理;删除临时文件、句柄、套接字等。不要尝试在测试文件之间共享状态或资源。
# 关于并行测试的注意事项
某些类型的测试不太适合并行运行。例如,极其时间敏感的测试,或对有限资源池进行 I/O 请求的测试(例如打开端口,或自动化浏览器窗口,命中测试数据库或远程服务器等)。
免费层的云 CI 服务可能无法为其构建代理提供合适的多个核心的容器或虚拟机。关于 CI 中预期的性能提升:您的里程可能会有所不同。在 .mocharc.js
中使用条件检查 process.env.CI
可能会有所帮助,并根据需要调整作业数。
大于可用 CPU 核心数的 作业数 不太可能(但并非不可能)看到性能提升。也就是说,尝试不同的作业数——没有一种万能的解决方案,您测试的独特特征将决定最佳作业数;甚至可能更少的作业更快!
# 并行模式工作进程 ID
v9.2.0 中新增
并行模式启动的每个进程都分配了一个唯一的 ID,从第一个启动的进程的 0 到第 N 个进程的 N-1。可以通过环境变量 MOCHA_WORKER_ID
在测试中访问此工作进程 ID。例如,它可以用于为每个测试进程分配不同的数据库、服务端口等。
# 根钩子插件
v8.0.0 中新增
在某些情况下,您可能希望在每个文件中的每个测试之前(或之后)使用 钩子。这些被称为根钩子。在 v8.0.0 之前,实现此目的的方法是使用 --file
结合根钩子(参见 上面的示例)。这在 v8.0.0 中仍然有效,但在并行模式下无效!因此,强烈建议不要使用此方法运行根钩子,并且将来可能会弃用。
根钩子插件是一个通过 --require
加载的 JavaScript 文件,它“注册”一个或多个将在所有测试文件中使用的根钩子。
在浏览器中,您可以通过 rootHooks
对象直接设置根钩子:mocha.setup({ rootHooks: {beforeEach() {...}} })
,参见 mocha.setup()
# 定义根钩子插件
根钩子插件文件是一个脚本,它通过 module.exports
导出 mochaHooks
属性。它通过 --require <file>
加载。
以下是一个使用 CJS 和 ESM 语法定义根钩子的简单示例。
# 使用 CommonJS
// test/hooks.js
exports.mochaHooks = {
beforeEach(done) {
// do something before every test
done();
}
};
# 使用 ES 模块
我们在这些示例中使用 .mjs
扩展名。
提示:如果您在使用 ES 模块时遇到问题,请参考 Node.js 文档。
// test/hooks.mjs
export const mochaHooks = {
beforeEach(done) {
// do something before every test
done();
}
};
注意:后面的示例将使用 ESM 语法。
# 可用的根钩子
根钩子适用于任何接口,但属性名称不会改变。换句话说,如果您使用的是 tdd
接口,则 suiteSetup
映射到 beforeAll
,setup
映射到 beforeEach
。
可用的根钩子及其行为
beforeAll
:- 在串行模式(Mocha 的默认模式)下,在所有测试开始之前,仅运行一次
- 在并行模式下,在所有测试开始之前,为每个文件运行
beforeEach
:- 在两种模式下,在每个测试之前运行
afterAll
:- 在串行模式下,在所有测试结束后,仅运行一次
- 在并行模式下,在所有测试结束后,为每个文件运行
afterEach
:- 在两种模式下,在每个测试之后运行
提示:如果您需要确保代码在任何模式下都只运行一次,请使用 全局夹具。
与其他钩子一样,this
指向当前上下文对象
// test/hooks.mjs
export const mochaHooks = {
beforeAll() {
// skip all tests for bob
if (require('os').userInfo().username === 'bob') {
return this.skip();
}
}
};
# 单个插件中的多个根钩子
为了组织目的,可以在单个插件中定义多个根钩子。例如
// test/hooks.mjs
export const mochaHooks = {
beforeEach: [
function (done) {
// do something before every test,
// then run the next hook in this array
},
async function () {
// async or Promise-returning functions allowed
}
]
};
# 根钩子插件可以导出一个函数
如果您需要执行一些逻辑 - 例如根据环境有条件地选择根钩子 - mochaHooks
可以是一个函数,它返回预期的对象。
// test/hooks.mjs
export const mochaHooks = () => {
if (process.env.CI) {
// root hooks object
return {
beforeEach: [
function () {
// CI-specific beforeEach
},
function () {
// some other CI-specific beforeEach
}
]
};
}
// root hooks object
return {
beforeEach() {
// regular beforeEach
}
};
};
如果您需要执行异步操作,mochaHooks
可以是返回 Promise
的。
// test/hooks.mjs
export const mochaHooks = async () => {
const result = await checkSomething();
// only use a root hook if `result` is truthy
if (result) {
// root hooks object
return {
beforeEach() {
// something
}
};
}
};
# 多个根钩子插件
可以通过多次使用 --require
来注册多个根钩子插件。例如,要注册 hooks-a.js
和 hooks-b.js
中的根钩子,请使用 --require hooks-a.js --require hooks-b.js
。这些将按顺序注册(并运行)。
# 迁移测试以使用根钩子插件
要将使用根钩子的测试迁移到根钩子插件
- 找到您的根钩子(在套件外部定义的钩子 - 通常是
describe()
回调)。 - 创建一个新文件,例如
test/hooks.js
。 - 移动您的根钩子到
test/hooks.js
中。 - 在
test/hooks.js
中,将您的钩子设为导出的mochaHooks
属性的成员。 - 在运行测试时使用
--require test/hooks.js
(更好的是:使用一个 配置文件,其中包含{"require": "test/hooks.js"}
)。
例如,给定以下文件 test/test.spec.js
,其中包含根钩子
// test/test.spec.js
beforeEach(function () {
// global setup for all tests
});
after(function () {
// one-time final cleanup
});
describe('my test suite', function () {
it('should have run my global setup', function () {
// make assertion
});
});
您的 test/hooks.js
(对于此示例,一个 CJS 模块)应该包含
// test/hooks.js
exports.mochaHooks = {
beforeEach: function () {
// global setup for all tests
},
afterAll: function () {
// one-time final cleanup
}
};
注意:小心!
after
变为afterAll
,before
变为beforeAll
。
您原来的 test/test.spec.js
现在应该包含
// test/test.spec.js
describe('my test suite', function () {
it('should have run my global setup', function () {
// make assertion
});
});
运行 mocha --require test/hooks.js test/test.spec.js
将像以前一样运行(现在可以与 --parallel
一起使用)。
# 迁移库以使用根钩子插件
如果您是库维护者,并且您的库使用根钩子,您可以通过重构您的入口点来迁移
- 您的库应该始终导出一个
mochaHooks
对象。 - 为了保持向后兼容性,仅当
global.beforeEach
(或其他相关钩子)存在时才运行您的根钩子。 - 指示您的用户在运行
mocha
时使用--require <your-package>
。
# 全局夹具
v8.2.0 中新增
乍一看,全局夹具似乎与 根钩子 类似。但是,与根钩子不同,全局夹具
- 保证仅执行一次
- 在并行模式、观察模式和串行模式下工作相同
- 不与测试、套件或其他钩子共享上下文
# 全局设置夹具
要创建全局设置夹具,请从脚本中导出 mochaGlobalSetup
,例如:
// fixtures.cjs
// can be async or not
exports.mochaGlobalSetup = async function () {
this.server = await startSomeServer({port: process.env.TEST_PORT});
console.log(`server running on port ${this.server.port}`);
};
…或 ES 模块
// fixtures.mjs
// can be async or not
export async function mochaGlobalSetup() {
this.server = await startSomeServer({port: process.env.TEST_PORT});
console.log(`server running on port ${this.server.port}`);
}
要使用它,请在通过 mocha --require fixtures.cjs
(或您为文件命名的任何名称)运行 Mocha 时加载此文件。
请记住:您可以在 配置文件 中定义“需要”。
现在,在 Mocha 加载并运行您的测试之前,它将执行上述全局设置夹具,启动一个用于测试的服务器。但是,这不会在 Mocha 完成时关闭服务器!要做到这一点,请使用 全局拆卸夹具。
# 全局拆卸夹具
就像 全局设置夹具 一样,全局拆卸夹具可以通过从“需要”的脚本中导出来创建(我们可以将两种类型的夹具放在一个文件中)
// fixtures.cjs, cont'd
// can be async or not
exports.mochaGlobalTeardown = async function () {
await this.server.stop();
console.log('server stopped!');
};
…或 ES 模块
// fixtures.mjs, cont'd
// can be async or not
export async function mochaGlobalTeardown() {
await this.server.stop();
console.log('server stopped!');
}
您会注意到我们在夹具示例中使用了 this
。全局设置夹具和全局拆卸夹具共享一个上下文,这意味着我们可以在设置夹具中向上下文对象(this
)添加属性,并在拆卸夹具中稍后引用它们。当夹具在单独的文件中时,这更有用,因为您可以只使用 JS 的变量作用域规则(下面的示例)。
如 上面 所述 - 以及 下面 - 测试文件无法访问此上下文对象。
# 何时使用全局夹具
全局夹具适用于启动服务器、打开套接字或以其他方式创建您的测试将通过 I/O 重复访问的资源。
# 何时不使用全局夹具
如果您需要访问内存中的值(例如文件句柄或数据库连接),不要使用全局夹具来执行此操作,因为您的测试将无法访问该值。
您可以很聪明地尝试通过将某些内容分配给
global
对象来解决此限制,但这在并行模式下不起作用。最好遵守规则!
相反,使用全局夹具来启动数据库,并使用 根钩子插件 或普通的 钩子 来创建连接。
以下是如何使用全局夹具和“全部之前”钩子来完成工作的示例。请注意,我们在测试中没有在任何地方引用 server
对象!
首先,使用全局夹具启动和停止测试服务器
// fixtures.mjs
let server;
export const mochaGlobalSetup = async () => {
server = await startSomeServer({port: process.env.TEST_PORT});
console.log(`server running on port ${server.port}`);
};
export const mochaGlobalTeardown = async () => {
await server.stop();
console.log('server stopped!');
};
然后,在您的测试中连接到服务器
// test.spec.mjs
import {connect} from 'my-server-connector-thingy';
describe('my API', function () {
let connection;
before(async function () {
connection = await connect({port: process.env.TEST_PORT});
});
it('should be a nice API', function () {
// assertions here
});
after(async function () {
return connection.close();
});
});
最后,使用此命令将它们整合在一起:mocha --require fixtures.mjs test.spec.mjs
。
# 测试夹具决策树向导
此流程图将帮助您确定应该使用哪种 钩子、根钩子插件 或 全局夹具。
# 接口
Mocha 的“接口”系统允许开发人员选择他们的 DSL 样式。Mocha 具有 BDD、TDD、Exports、QUnit 和 Require 样式的接口。
# BDD
BDD 接口提供 describe()
、context()
、it()
、specify()
、before()
、after()
、beforeEach()
和 afterEach()
。
context()
只是 describe()
的别名,并且行为相同;它提供了一种使测试更易于阅读和组织的方法。类似地,specify()
是 it()
的别名。
所有前面的示例都是使用 BDD 接口编写的。
describe('Array', function () {
before(function () {
// ...
});
describe('#indexOf()', function () {
context('when not present', function () {
it('should not throw an error', function () {
(function () {
[1, 2, 3].indexOf(4);
}).should.not.throw();
});
it('should return -1', function () {
[1, 2, 3].indexOf(4).should.equal(-1);
});
});
context('when present', function () {
it('should return the index where the element first appears in the array', function () {
[1, 2, 3].indexOf(3).should.equal(2);
});
});
});
});
# TDD
TDD 接口提供 suite()
、test()
、suiteSetup()
、suiteTeardown()
、setup()
和 teardown()
suite('Array', function () {
setup(function () {
// ...
});
suite('#indexOf()', function () {
test('should return -1 when not present', function () {
assert.equal(-1, [1, 2, 3].indexOf(4));
});
});
});
# Exports
Exports 接口与 Mocha 的前身 expresso 非常相似。键 before
、after
、beforeEach
和 afterEach
是特殊情况,对象值是套件,函数值是测试用例
module.exports = {
before: function () {
// ...
},
Array: {
'#indexOf()': {
'should return -1 when not present': function () {
[1, 2, 3].indexOf(4).should.equal(-1);
}
}
}
};
# QUnit
受 QUnit 启发的接口与 QUnit 的“扁平”外观相匹配,其中测试套件标题定义在测试用例之前。与 TDD 一样,它使用 suite()
和 test()
,但类似于 BDD,它还包含 before()
、after()
、beforeEach()
和 afterEach()
。
function ok(expr, msg) {
if (!expr) throw new Error(msg);
}
suite('Array');
test('#length', function () {
var arr = [1, 2, 3];
ok(arr.length == 3);
});
test('#indexOf()', function () {
var arr = [1, 2, 3];
ok(arr.indexOf(1) == 0);
ok(arr.indexOf(2) == 1);
ok(arr.indexOf(3) == 2);
});
suite('String');
test('#length', function () {
ok('foo'.length == 3);
});
# Require
require
接口允许您使用 require
直接要求 describe
和朋友词,并随意调用它们。如果您想避免在测试中使用全局变量,此接口也很有用。
注意:require
接口不能通过 node
可执行文件运行,必须通过 mocha
运行。
var testCase = require('mocha').describe;
var pre = require('mocha').before;
var assertions = require('mocha').it;
var assert = require('chai').assert;
testCase('Array', function () {
pre(function () {
// ...
});
testCase('#indexOf()', function () {
assertions('should return -1 when not present', function () {
assert.equal([1, 2, 3].indexOf(4), -1);
});
});
});
# 报告器
Mocha 报告器会适应终端窗口,并且当 stdio 流与 TTY 不相关联时始终禁用 ANSI 转义颜色。
# Spec
别名:Spec
、spec
这是默认的报告器。Spec 报告器输出一个分层视图,嵌套方式与测试用例相同。
# 点矩阵
别名:Dot
、dot
点矩阵报告器是一系列代表测试用例的字符。失败以红色感叹号(!
)突出显示,待定测试以蓝色逗号(,
)突出显示,慢速测试以黄色突出显示。如果您更喜欢最少的输出,则效果很好。
# Nyan
别名:Nyan
、nyan
Nyan 报告器正是您所期望的
# TAP
别名:TAP
、tap
TAP 报告器为 Test-Anything-Protocol 消费者发出行。
# 着陆带
别名:Landing
、landing
着陆带报告器是一个花哨的测试报告器,模拟飞机着陆 😃 unicode ftw
# 列表
别名:List
、list
列表报告器输出一个简单的规范列表,作为测试用例通过或失败,在输出底部输出失败详细信息。
# 进度
别名:Progress
、progress
进度报告器实现了一个简单的进度条
# JSON
别名:JSON
、json
JSON 报告器在测试完成后(无论失败与否)输出一个大型 JSON 对象。
默认情况下,它将输出到控制台。要直接写入文件,请使用 --reporter-option output=filename.json
。
# JSON 流
别名:JSONStream
、json-stream
JSON 流报告器在它们发生时输出换行符分隔的 JSON“事件”,从“开始”事件开始,然后是测试通过或失败,最后是最终的“结束”事件。
# Min
别名:Min
、min
Min 报告器仅显示摘要,同时在失败时仍输出错误。此报告器与 --watch
配合使用效果很好,因为它会清除终端以将测试摘要保留在顶部。
# Doc
别名:Doc
、doc
Doc 报告器输出测试的分层 HTML 主体表示。用标题、页脚和一些样式将其包装起来,然后您将获得一些很棒的文档!
例如,假设您有以下 JavaScript
describe('Array', function () {
describe('#indexOf()', function () {
it('should return -1 when the value is not present', function () {
[1, 2, 3].indexOf(5).should.equal(-1);
[1, 2, 3].indexOf(0).should.equal(-1);
});
});
});
命令 mocha --reporter doc array
将产生
<section class="suite">
<h1>Array</h1>
<dl>
<section class="suite">
<h1>#indexOf()</h1>
<dl>
<dt>should return -1 when the value is not present</dt>
<dd>
<pre><code>[1,2,3].indexOf(5).should.equal(-1);
[1,2,3].indexOf(0).should.equal(-1);</code></pre>
</dd>
</dl>
</section>
</dl>
</section>
SuperAgent 请求库的 测试文档 是使用 Mocha 的 doc 报告器使用此 Bash 命令生成的
$ mocha --reporter=doc | cat docs/head.html - docs/tail.html > docs/test.html
查看 SuperAgent 的 Makefile 以供参考。
# Markdown
别名:Markdown
、markdown
Markdown 报告器为您的测试套件生成一个 Markdown TOC 和主体。如果您想在 Github wiki 页面中使用测试作为文档,或者在 Github 可以渲染的存储库中的 Markdown 文件中使用测试,这非常有用。例如,这是 Connect 的 测试输出。
# XUnit
别名:XUnit
、xunit
XUnit 报告器也可用。它输出一个与 XUnit 兼容的 XML 文档,通常适用于 CI 服务器。
默认情况下,它将输出到控制台。要直接写入文件,请使用 --reporter-option output=filename.xml
。
要指定自定义报告标题,请使用 --reporter-option suiteName="Custom name"
。
# 第三方报告器
Mocha 允许您定义自定义报告器。有关更多信息,请参阅 wiki。
示例
- TeamCity 报告器
- 我们的 工作示例
# HTML 报告器
别名:HTML
、html
HTML 报告器不适合在命令行中使用。
# Node.JS 原生 ESM 支持
v7.1.0 新功能
Mocha 现在支持将您的测试编写为 ES 模块,而不仅仅是使用 CommonJS。例如
// test.mjs
import {add} from './add.mjs';
import assert from 'assert';
it('should add to numbers from an es module', () => {
assert.equal(add(3, 5), 8);
});
要启用此功能,您无需执行任何特殊操作。将您的测试文件编写为 ES 模块。在 Node.js 中,这意味着将文件扩展名更改为 .mjs
,或者如果您想使用常规的 .js
扩展名,则在您的 package.json
中添加 "type": "module"
。更多信息可以在 Node.js 文档 中找到。
# 当前限制
- 监视模式 不支持 ES 模块测试文件
- 自定义报告器 和 自定义接口 只能是 CommonJS 文件
- 配置文件 只能是 CommonJS 文件 (
.mocharc.js
或.mocharc.cjs
) - 当使用像
proxyquire
、rewiremock
或rewire
这样的库进行模块级模拟时,请不要使用 ES 模块作为您的测试文件。您可以切换到使用testdouble
,它支持 ESM。
# 在浏览器中运行 Mocha
Mocha 在浏览器中运行。Mocha 的每个版本都会有新的 ./mocha.js
和 ./mocha.css
构建,供在浏览器中使用。
一个典型的设置可能如下所示,我们调用 mocha.setup('bdd')
来使用 BDD 接口,然后加载测试脚本,并在 onload
时使用 mocha.run()
运行它们。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mocha Tests</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="https://unpkg.com/chai/chai.js"></script>
<script src="https://unpkg.com/mocha/mocha.js"></script>
<script class="mocha-init">
mocha.setup('bdd');
mocha.checkLeaks();
</script>
<script src="test.array.js"></script>
<script src="test.object.js"></script>
<script src="test.xhr.js"></script>
<script class="mocha-exec">
mocha.run();
</script>
</body>
</html>
# Grep
浏览器可以使用 --grep
作为功能。将查询字符串追加到您的 URL:?grep=api
。
# 浏览器配置
Mocha 选项可以通过 mocha.setup()
设置。示例
// Use "tdd" interface. This is a shortcut to setting the interface;
// any other options must be passed via an object.
mocha.setup('tdd');
// This is equivalent to the above.
mocha.setup({
ui: 'tdd'
});
// Examples of options:
mocha.setup({
allowUncaught: true,
asyncOnly: true,
bail: true,
checkLeaks: true,
dryRun: true,
failZero: true,
forbidOnly: true,
forbidPending: true,
global: ['MyLib'],
retries: 3,
rootHooks: { beforeEach(done) { ... done();} },
slow: '100',
timeout: '2000',
ui: 'bdd'
});
# 浏览器特定选项
浏览器 Mocha 支持许多,但并非所有 cli 选项。要使用包含“ - ”的 cli 选项,请将选项转换为驼峰式命名法 (例如,check-leaks
转换为 checkLeaks
)。
# 与 cli 选项 略有不同的选项
reporter
{string|constructor} 您可以传递报告器的名称或自定义报告器的构造函数。您可以找到浏览器 此处 的 推荐 报告器。也可以使用 内置报告器。它们在浏览器中的使用既不推荐也不支持,请打开控制台查看测试结果。
# 仅在浏览器上下文中起作用的选项
noHighlighting
{boolean} 如果设置为 true
,则不要尝试在输出测试代码上使用语法高亮。
# 报告
在浏览器中运行 Mocha 时,HTML 报告器是默认报告器。它看起来像这样
Mochawesome 是默认 HTML 报告器的绝佳替代方案。
# 配置 Mocha (Node.js)
v6.0.0 中新增
Mocha 支持配置文件,这在现代命令行工具中很常见,并且支持多种格式
- JavaScript:在项目的根目录中创建一个
.mocharc.js
(或在您的package.json
中使用"type"="module"
时创建一个.mocharc.cjs
),并导出一个包含您的配置的对象 (module.exports = {/* ... */}
)。 - YAML:在项目的根目录中创建一个
.mocharc.yaml
(或.mocharc.yml
)。 - JSON:在项目的根目录中创建一个
.mocharc.json
(或.mocharc.jsonc
)。注释 - 虽然不是有效的 JSON - 在此文件中是允许的,并且会被 Mocha 忽略。 - package.json:在项目的
package.json
中创建一个mocha
属性。
# 自定义位置
您可以使用 --config <path>
选项指定配置文件的自定义位置。Mocha 将使用文件的扩展名来确定如何解析文件,如果未知,将假定为 JSON。
您也可以使用 --package <path>
选项指定自定义 package.json
位置。
# 忽略配置文件
要跳过查找配置文件,请使用 --no-config
。同样,使用 --no-package
阻止 Mocha 在 package.json
中查找配置。
# 优先级
如果没有给出自定义路径,并且如果同一个目录中存在多个配置文件,Mocha 将只搜索并使用其中一个。优先级是
.mocharc.js
.mocharc.yaml
.mocharc.yml
.mocharc.jsonc
.mocharc.json
# 合并
Mocha 还会将 package.json
中找到的任何选项合并到其运行时配置中。如果发生冲突,优先级是
- 命令行上指定的参数
- 配置文件 (
.mocharc.js
、.mocharc.yml
等) package.json
的mocha
属性
可以安全重复的选项 (例如,--require
) 将被连接,优先级更高的配置源将出现在列表的前面。例如,一个 .mocharc.json
包含 "require": "bar"
,再加上执行 mocha --require foo
,会导致 Mocha 按顺序要求 foo
,然后要求 bar
。
# 扩展配置
配置可以使用 extends
关键字从其他模块继承。有关更多信息,请参见 此处。
# 配置格式
- 任何“布尔”标志 (不需要参数,例如
--bail
) 都可以使用布尔值指定,例如:"bail": true
。 - 任何“数组”类型选项 (有关列表,请参见
mocha --help
) 都可以是单个字符串值。 - 对于包含破折号 (
-
) 的选项,选项名称可以使用驼峰式命名法指定。 - 别名是有效的名称,例如,
R
代替reporter
。 - 测试文件可以使用
spec
指定,例如,"spec": "test/**/*.spec.js"
。 node
的标志也支持在配置文件中使用。请谨慎使用,因为这些标志可能在不同版本的 Node.js 之间有所不同!
有关更多配置示例,请参见 GitHub 上的 example/config
目录。
# test/
目录
默认情况下,mocha
查找 glob "./test/*.{js,cjs,mjs}"
,因此您可能希望将测试放在 test/
文件夹中。如果您想包含子目录,请传递 --recursive
选项。
要配置 mocha
查找测试的位置,您可以传递自己的 glob
$ mocha --recursive "./spec/*.js"
一些 shell 支持使用 globstar (**
) 通配符进行递归匹配。Bash >= 4.3 支持使用 globstar
选项,该选项 必须启用 才能获得与传递 --recursive
选项相同的结果 (ZSH 和 Fish 默认支持此功能)。启用递归匹配后,以下内容与传递 --recursive
相同
$ mocha "./spec/**/*.js"
您应该始终在 npm 脚本中引用您的 glob。如果您使用引号,node-glob
模块将处理其扩展。为了最大限度地兼容,请将整个表达式用双引号括起来,并在表达式中避免使用 $
、"
、^
和 \
。
请参见此 教程,了解如何使用 glob。
注意:建议将 glob 放在双引号中以确保可移植性。
# 错误代码
v6.0.0 中新增
当 Mocha 本身抛出异常时,关联的 Error
将具有 code
属性。在适用情况下,使用者应该检查 code
属性,而不是对 message
属性进行字符串匹配。下表描述了这些错误代码
代码 | 描述 |
---|---|
ERR_MOCHA_INVALID_ARG_TYPE | 为给定参数传递了错误类型 |
ERR_MOCHA_INVALID_ARG_VALUE | 为给定参数传递了无效或不支持的值 |
ERR_MOCHA_INVALID_EXCEPTION | 抛出了一个虚假或未充分指定的异常 |
ERR_MOCHA_INVALID_INTERFACE | 选项中指定的接口未找到 |
ERR_MOCHA_INVALID_REPORTER | 选项中指定的报告器未找到 |
ERR_MOCHA_NO_FILES_MATCH_PATTERN | 找不到测试文件 |
ERR_MOCHA_UNSUPPORTED | 请求的行为、选项或参数不受支持 |
# 编辑器插件
以下与编辑器相关的软件包可用
# TextMate
Mocha TextMate 包 包含代码片段,可以使编写测试更快更愉快。
# JetBrains
JetBrains 为其 IDE 套件 (IntelliJ IDEA、WebStorm 等) 提供了一个 NodeJS 插件,其中包含 Mocha 测试运行器,以及其他功能。
该插件名为 NodeJS,可以通过 Preferences > Plugins 安装,前提是您的许可证允许这样做。
# Wallaby.js
Wallaby.js 是一种持续测试工具,它为 VS Code、Atom、JetBrains IDE (IntelliJ IDEA、WebStorm 等)、Sublime Text 和 Visual Studio 中的 Mocha 提供实时代码覆盖率,适用于浏览器和 node.js 项目。
# Emacs
通过第三方软件包 mocha.el 可以使用 Emacs 支持运行 Mocha 测试。该软件包在 MELPA 上可用,可以通过 M-x package-install mocha
安装。
# Mocha 侧边栏 (VS Code)
Mocha 侧边栏 是 VS Code 最完整的 mocha 扩展。
# 功能
- 在 VS Code 侧边栏菜单中查看所有测试
- 运行和调试从所有测试到单个测试 (以及每个套件) 的每个级别层次结构的测试
- 在文件保存时自动运行测试
- 直接在代码编辑器中查看测试结果
# 示例
真实的示例代码
# 测试 Mocha
要运行 Mocha 的测试,您需要 GNU Make 或兼容的工具;Cygwin 应该可以工作。
$ cd /path/to/mocha
$ npm install
$ npm test
# 更多信息
除了在 我们的 Discord 上与我们聊天之外,有关使用间谍、模拟和共享行为等更多信息,请务必查看 GitHub 上的 Mocha Wiki。有关 Mocha 的运行示例,请查看 example/tests.html。有关 JavaScript API,请查看 API 文档 或 源代码。