当前位置: 代码迷 >> 综合 >> Mocha.js官方文档翻译 —— 简单、灵活、有趣
  详细解决方案

Mocha.js官方文档翻译 —— 简单、灵活、有趣

热度:88   发布时间:2023-09-30 05:22:40.0

1. 简介

Mocha是一个能够运行在Node和浏览器中的多功能的JavaScript测试框架,它让异步测试简单且有趣。Mocha连续地运行测试,并给出灵活而精确的报告,同时能够将错误精确地映射到测试用例上。它托管在GitHub上。

2. 开始使用

2.1 安装

npm 全局安装:

npm install --global mocha

或者作为开发依赖安装在项目中:

npm install mocha --save-dev

安装Mocha v3.0.0或者更新的版本,你需要v1.4.0或者更新版本的npm。此外,运行Mocha的Node版本不能低于v0.10

Mocha也能通过Bower安装,还可通过cdnjs进行引用。

2.2 起步

npm install mocha
mkdir test
$EDITOR test/test.js # 或者使用你喜欢的编辑器打开

在编辑器中:

var assert = require('assert')
describe('Array', function () {describe('#indexOf()', function() {it('未找到值时应当返回-1', function () {assert.equal(-1, [1, 2, 3].indexOf(4))})})
})

回到命令行:

mocha test.js
//输出如下
Array#indexOf()√ 未找到值时应当返回-11 passing (9ms)

上面这段代码,就是测试脚本,它可以独立执行。测试脚本里面应该包括一个或多个describe块,每个describe块应该包括一个或多个it块。

describe块称为"测试套件"(test suite),表示一组相关的测试。它是一个函数,第一个参数是测试套件的名称(“Array”),第二个参数是一个实际执行的函数。

it块称为"测试用例"(test case),表示一个单独的测试,是测试的最小单位。它也是一个函数,第一个参数是测试用例的名称("#indexOf()"),第二个参数是一个实际执行的函数。

2.3 断言

Mocha允许你使用你喜欢的断言库。在之后的例子中,我们使用了Node中内置的断言模块——但通常情况下,只要它能抛出异常就行。这意味着你可以使用下列断言库:

  • should.js - BDD风格贯穿始终
  • expect.js - expect() 风格的断言
  • chai - expect()、assert()和 should风格的断言‘
  • better-assert - C风格的自文档化的assert()
  • unexpected - “可扩展的BDD断言工具”

2.4 异步代码

用Mocha测试异步代码简单的不要不要的!测试运行完了调用一下回调函数就行。只需要在it()中添加一个回调,Mocha就知道应该等到这个回调被调用时才结束这个测试用例的运行。

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()函数接受一个error参数,所以上面的代码可以这么写:

describe('User', function() {describe('#save()', function() {it('should save without error', function(done) {var user = new User('Luna');user.save(done);});});
});
Promise

有时,与其使用done()回调函数,你会想在你的异步代码中返回一个Promise,当你正在测试的API是返回一个Promise而不是使用回调时这会很有帮助:

beforeEach(function () {return db.clear().then(function () {return db.save([tobi, loki, jane])})
})describe('#find()', function () {it('返回匹配的记录', function () {return db.find({ type: 'User' }).should.eventually.have.length(3)})
})

接下来的例子将会使用chai-as-promised来获得流畅的promise断言

在Mocha 3.0及更新的版本中,同时返回一个Promise和调用done()会导致一个异常,下面的代码是错误的:

const assert = require('assert')it('应该结束这个测试用例', 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()会被忽略(而不会报错)。

2.5 同步代码

当测试同步代码的时候,省略回调函数,Mocha就会自动执行下一条测试。

describe('Array', function () {describe('#indexOf()', function () {it('没有找到时应当返回-1', function () {[1, 2, 3].indexOf(5).should.equal(-1)[1, 2, 3].indexOf(0).should.equal(-1)})})
})

2.6 箭头函数

不建议在Mocha中使用箭头函数(“lambdas”)。由于(箭头函数特殊的)this绑定语法,箭头函数无法访问Mocha的上下文。例如,下面的代码会因为使用了箭头函数而执行失败:

describe('my suit', () => {it('my test', () => {// 应当设置1000毫秒延迟,而不是执行失败this.timeout(1000)assert.ok(true)})
})

当然如果你不需要使用Mocha的上下文,使用lambdas就没有问题了。然而这样的做的结果是你的测试代码将难以重构

2.7 钩子函数

Mocha默认使用“BDD”风格的接口,提供了before()after()beforeEach()afterEach()四个钩子函数。这些函数可以用来(在测试前)做预处理工作或在测试后清理工作。

describe('hooks', function () {before(function () {// 在这个作用域的所有测试用例运行之前运行})after(function () {// 在这个作用域的所有测试用例运行完之后运行})beforeEach(function () {// 在这个作用域的每一个测试用例运行之前运行})afterEach(function () {// 在这个作用域的每一个测试用例运行之后运行})// 测试用例
})

测试用例和测试的钩子可以混合排列。(相同的)钩子函数会按照它们的书写顺序运行;(整体的运行顺序是)所有的before()钩子运行一次,然后是beforeEach()钩子,测试用例,afterEach()钩子(循环运行),最后是after()钩子(运行一次)

钩子函数的描述参数

所有的钩子在调用时都可以提供一个可选的“描述信息”的参数,以便在你的测试中更精确地定位错误。如果给一个钩子函数传入一个命名函数,当未提供“描述信息”参数的时候,这个命名函数的名称将被作为描述信息。

beforeEach(function () {// beforeEach hook
})beforeEach(function namedFun () {// beforeEach: namedFun
})beforeEach('一些描述信息' ,function () {// beforEach: 一些描述信息
})
异步钩子

所有钩子(before()after()beforeEach()afterEach())既可以是同步的也可以是异步的,(这一点上)它们的行为与普通的测试用例非常相似:

describe('连接', function () {var db = new Connection,tobi = new User('tobi'),loki = new User('loki'),jane = newUser('jane')beforeEach(function (done) {db.clear(function (err) {if (err) return done(err)db.save([tobi, loki, jane], done)})})describe('#find()', function () {it('返回匹配的记录', function (done) {db.find({type: 'User'}, function (err, res) {if (err) return done(err)res.should.have.length(3)})})})
})
全局钩子

你可以在任意(测试)文件中添加“全局”级别的钩子函数,例如,在所有describe()作用域之外添加一个beforeEach(),它的回调函数会在所有的测试用例运行之前运行,无论(这个测试用例)处在哪个文件(这是因为Mocha有一个隐藏的describe()作用域,称为“根测试套件 root suite”)。

beforeEach(function () {console.log('在所有文件的所有测试用例之前运行')
})
延迟的根测试套件

如果你需要在所有测试套件运行之前进行一些异步操作,你可以延迟根测试套件。以--delay参数运行mocha,这会在全局注入一个特殊的回调函数run()

setTimeout(function () {// 一些设置describe('我的测试套件', function () {// ...})run()
}, 5000)

2.8 挂起测试(Pending Tests)

“Pending”——“有人最终会编写这些测试用例”——没有传入回调函数的测试用例:

describe('Array', function () {describe('#indexOf()', function () {// 挂起的测试用例it('未找到时应当返回-1')})
})

挂起的测试用例会在报告中出现“pending”状态。

2.9 独占测试

通过向测试套件或测试用例函数添加.only后缀,独占特性允许你只运行指定的测试套件或测试用例。下面是一个独占测试套件的例子:

describe('Array', function (){describe.only('#indexOf()', function () {// ...})
})

注意:所有嵌套(在.only套件中的)测试套件仍旧会运行
下面是一个运行单个测试用例的例子:

describe('Array', function (){describe('#indexOf()', function () {it.only('除非找到否则返回-1', function () {// ...})it('找到后应当返回下标', function () {// ...})})
})

在v3.0.0版本以前,only()使用字符串匹配来决定哪些测试需要执行。v3.0.0以后的版本only()可以使用多次来定义测试用例的子集去运行:

describe('Array', function() {describe('#indexOf()', function() {it.only('should return -1 unless present', function() {// 这个测试用例会运行})it.only('should return the index when present', function() {// 这个测试用例也会运行})it('should return -1 if called with a non-Array context', function() {// 这个测试用例不会运行})})
})

你也可以选择多个测试套件:

describe('Array', function() {describe.only('#indexOf()', function() {it('should return -1 unless present', function() {// 这个测试用例会运行})it('should return the index when present', function() {// 这个测试用例也会运行})})describe.only('#concat()', function () {it('should return a new Array', function () {// 这个测试用例也会运行})})describe('#slice()', function () {it('should return a new Array', function () {// 这个测试用例不会运行})})
})

但测试会存在优先级:

describe('Array', function() {describe.only('#indexOf()', function() {it.only('should return -1 unless present', function() {// 这个测试用例会运行})it('should return the index when present', function() {// 这个测试用例不会运行})})
})

注意,如果提供了钩子函数,钩子函数仍会执行

注意不要把 .only 提交到版本控制上,除非你明确知道你在做什么

2.10 跳过测试

这个功能是only()的反面。通过后缀skip()就可以让Mocha忽略这个测试套件或测试用例。所有被跳过的测试都会被标记为pending状态并体现在报告中。下面是一个跳过一整个测试套件的例子:

describe('Array', function() {describe.skip('#indexOf()', function() {// ...})
})

下面是一个跳过测试用例的例子:

describe('Array', function() {describe('#indexOf()', function() {it.skip('should return -1 unless present', function() {// 这个测试用例不会运行})it('should return the index when present', function() {// 这个测试用例会运行})})
})

最佳实践:使用skip()而不是直接将测试注释掉

你也可以使用this.skip()在运行时跳过测试。如果测试需要的环境或配置没办法提前检测,可以考虑使用运行时跳过。例如:

it('应该仅在正确的环境配置中测试', function () {if(/*测试环境正确*/) {// 编写断言} else {this.skip()}
})

因为这个测试什么也没做,它会被报告为passing

最佳实践:不要什么也不做!一个测试应当编写断言或者使用this.skip()

如果想以这种方式跳过多个测试,可以在一个befor()钩子函数中调用this.skip()

before(function() {if (/* check test environment */) {// setup code} else {this.skip()}
})

在Mocha v3.0.0版本以前,钩子函数和异步测试中不支持this.skip()

2.11 重试测试

你可以选择将失败的测试重试一定的次数。这个特性被设计用于资源(数据)不容易被仿造的端到端(end-to-end)测试(functional tests/Selenium…)。不推荐将这个特性用于单元测试

这个特性会重新运行beforeEach()/afterEach()钩子,但不会运行before()/after()钩子。

注意:下面的例子使用了Selenium webdriver(为Promise链式调用改写了Mocha的全局钩子)。

describe('retries', function() {// 测试套件中的所有测试用例将被重试4次this.retries(4)beforeEach(function () {browser.get('http://www.yahoo.com');})it('should succeed on the 3rd try', function () {// 指定这个测试用例仅重试2次this.retries(2)expect($('.foo').isDisplayed()).to.eventually.be.true})
})

2.12 动态生成测试

可以使用Function.prototype.call和函数表达式来定义测试套件和测试用例,以动态生成测试而不需要其它的特殊语法——简单的JavaScript就能用于实现你可能在其它测试框架中见到过的类似“参数化”测试的功能。

例如:

var assert = require('chai').assertfunction add() {return Array.prototype.slice.call(arguments).reduce(function(prev, curr) {return prev + curr}, 0)
}describe('add()', function() {var tests = [{args: [1, 2],       expected: 3},{args: [1, 2, 3],    expected: 6},{args: [1, 2, 3, 4], expected: 10}]tests.forEach(function(test) {it('correctly adds ' + test.args.length + ' args', function() {var res = add.apply(null, test.args)assert.equal(res, test.expected)})})
})

上面的代码将会生成一个带有三个测试用例的测试套件:

$ mochaadd()? correctly adds 2 args? correctly adds 3 args? correctly adds 4 args

2.13 测试耗时

许多测试报告都会显示测试耗时,并且标记出那些耗时较长的测试,就像下面的报告显示的那样:

Mocha.js官方文档翻译 —— 简单、灵活、有趣

测试耗时

你可以使用slow()方法来定义到底多久才算“耗时较长”:

describe('something slow', function() {this.slow(10000)it('它的耗时应该足够我去做个三明治了', function() {// ...})
})

2.14 测试超时

套件级别

套件级别的超时应用于整个测试套件,你也可以通过this.timeout(0)来取消超时限制。如果没有覆盖这个值的话,所有嵌套的测试套件和测试用例都会继承这个超时限制。

describe('a suite of tests', function() {this.timeout(500)it('应当不超过500毫秒', function(done){setTimeout(done, 300)})it('也应当不超过500毫秒', function(done){setTimeout(done, 250)})
})
用例级别

也可以对单一用例设置超时时间,或者通过this.timeout(0)来取消超时限制:

it('应该不超过500毫秒', function(done){this.timeout(500)setTimeout(done, 300)
})
钩子级别

当然也可以设置钩子级别的超时:

describe('一个测试套件', function() {beforeEach(function(done) {this.timeout(3000); // 一个用时很长的环境设置操作.setTimeout(done, 2500)})
})

同样,使用this.timeout(0)来取消超时限制

在v3.0.0或更新的版本中,给this.timeout()传递一个大于最大延迟值的参数会让超时限制失效

2.15 差异比较

Mocha支持断言库抛出的AssertionErrors的两个属性err.expectederr.actual。Mocha会尝试显示期望(的代码)和断言库真正看到的的代码之间的差异。这里有一个“string”差异的例子:

Mocha.js官方文档翻译 —— 简单、灵活、有趣

3. 通配符

命令行指定测试脚本时,可以使用通配符,同时指定多个文件。

$ mocha spec/{my,awesome}.js
$ mocha test/unit/*.js

上面的第一行命令,指定执行spec目录下面的my.js和awesome.js。第二行命令,指定执行test/unit目录下面的所有js文件。

除了使用Shell通配符,还可以使用Node通配符。

$ mocha 'test/**/*.@(js|jsx)'

上面代码指定运行test目录下面任何子目录中、文件后缀名为js或jsx的测试脚本。注意,Node的通配符要放在单引号之中,否则星号(*)会先被Shell解释。

上面这行Node通配符,如果改用Shell通配符,要写成下面这样。

$ mocha test/{,**/}*.{js,jsx}

4. 命令行

Usage: mocha [debug] [options] [files]Commands:init <path>  initialize a client-side mocha setup at <path>Options:-h, --help                              显示使用帮助-V, --version                           显示版本信息-A, --async-only                        强制所有测试带有回调(异步)或返回一个promise-c, --colors                            强制启用颜色-C, --no-colors                         强制关闭颜色-G, --growl                             启用弹出消息-O, --reporter-options <k=v,k2=v2,...>  测试报告工具详细设置-R, --reporter <name>                   指定测试报告工具-S, --sort                              测试文件排序-b, --bail                              第一次测试不通过立即结束测试-d, --debug                             开启node的debugger模式, 同node --debug-g, --grep <pattern>                    只运行匹配<pattern>的测试-f, --fgrep <string>                    只运行包含<string>的测试-gc, --expose-gc                        暴露gc扩展-i, --invert                            反转--grep 和--fgrep 匹配-r, --require <name>                    加载指定模块-s, --slow <ms>                         以毫秒为单位定义"慢" 测试门槛 [75]-t, --timeout <ms>                      以毫秒为单位设置测试用例超时时间 [2000]-u, --ui <name>                         指定用户接口 (bdd|tdd|qunit|exports)-w, --watch                             监测文件变动--check-leaks                           检查全局变量泄露--full-trace                            显示完整的跟踪堆栈--compilers <ext>:<module>,...          使用指定的模块编译文件--debug-brk                             在首行启用node的debugger断点--globals <names>                       allow the given comma-delimited global [names](没办法强行翻译了)--es_staging                            开启所有过时的特性--harmony<_classes,_generators,...>     all node --harmony* flags are available--preserve-symlinks                     命令模块加载器在解析和缓存模块时保留符号链接--icu-data-dir                          包括ICU数据--inline-diffs                          在行内显示实际/预期的字符差异--interfaces                            显示可用的接口(bdd|tdd|qunit|exports)--no-deprecation                        禁用警告--no-exit                               请求一个彻底的事件循环终止: Mocha不会调用 process.exit--no-timeouts                           禁用超时, 隐含 --debug--opts <path>                           指定选项路径--perf-basic-prof                       enable perf linux profiler (basic support)--prof                                  记录统计分析信息--log-timer-events                      记录包含外部回调的时间点--recursive                             包含子目录--reporters                             显示可用的测试报告工具--retries <times>                       设置重试未通过的测试用例的次数--throw-deprecation                     当使用了废弃的方法时抛出异常--trace                                 追踪函数借调--trace-deprecation                     显示废弃的跟踪堆栈--use_strict                            强制严格模式--watch-extensions <ext>,...            --watch 上附加的监控扩展--delay                                 等待异步套件定义

-w--watch

初始化后,监测文件变动运行测试

--compilers

CoffeeScript不再被直接支持。这类预编译语言可以使用相应的编译器扩展来使用,比如CS1.6:--compilers coffee:coffee-script 和CS1.7+:--compilers coffee:coffee-script/register

babel-register

如果你的ES6模块是以.js为扩展名的,你可以npm install --save-dev babel-register,然后--require babel-register; --compilers就可以指定文件扩展名

-b--ball

只对首个异常感兴趣?使用--bail

-d--debug

开启node的调试模式,这会用node debug <file ...>来执行你的脚本,允许你逐行调试代码并用debugger声明来打断点。注意mocha debugmocha --debug的区别:mocha debug会启动node内置的debug客户端,mocha --debug则允许你使用其它调试工具——比如Blink Developer Tools。

--globals <name>

接受一个以逗号分隔的全局变量名,例如,如果你的应用有意暴露一个全局变量名appYUI,你可能就会使用--globals app,YUI。它还接受通配符。--globals '*bar'会匹配foobar, barbar等。你也可以简单地传入*来忽略所有全局变量。

check-leaks

在运行测试时,Mocha默认不会检查全局变量泄露,可以使用--check-leaks来开启这一功能,使用--globals来指定接受的全局变量比如--globals jQuery,MyLib

-r--require <module-name>

--require选项对诸如should.js一类的库很有用,所以你可以使用--require should而不是在每一个测试文件中都调用require('should')。需要注意的是因为should是增强了Object.prototype所以可以正常使用,然而假如你希望访问某模块的输出你就只能require它们了,比如var should = require('should')。此外,还可以使用相对路径,比如--reqiure ./test/helper.js

-u--ui <name>

--ui选项让你指定想使用的接口,默认为“bdd”。

-R--reporter <name>

--reporter选项允许你指定希望使用的报告器,默认为“spec”。这个标记也可以用来使用第三方的报告器。例如,如果你npm install mocha-locv-reporter,你可以--reporter mocha-locv-reporter

-t--timeout <ms>

指定测试用例超时时间,默认为2秒。你可以传入毫秒数或者一个带s单位后缀的秒数进行覆盖,例如--timeout 2s--timeout 2000是等价的。

s--slow <ms>

指定“慢”测试阈值,默认为75毫秒。Mocha用这个去高亮那些耗时过长的测试。

-g--grep <pattern>

指定--grep选项让Mocha只运行匹配<pattern>的测试,<pattern>将会作为正则表达式进行解析。

假如,像下面的片段那样,你有一些“api”相关的测试和一些“app”相关的测试;则前者可以使用--grep api来运行,后者可使用--grep --app来运行

describe('api', function() {describe('GET /api/users', function() {it('respond with an array of users', function() {// ...})})
})describe('app', function() {describe('GET /users', function() {it('respond with an array of users', function() {// ...})})
})

配置文件mocha.opts
Mocha允许在test目录下面,放置配置文件mocha.opts,把命令行参数写在里面。例如:

$ mocha --recursive --reporter tap --growl

上面这个命令有三个参数--recursive--reporter tap--growl
然后,把这三个参数写入test目录下的mocha.opts文件。

--reporter tap
--recursive
--growl

然后,执行mocha就能取得与第一行命令一样的效果。

$ mocha

如果测试用例不是存放在test子目录,可以在mocha.opts写入以下内容。

server-tests
--recursive

上面代码指定运行server-tests目录及其子目录之中的测试脚本。

5.接口

Mocha的“接口”系统允许开发者选择习惯的风格或DSL。Mocha有BDDTDDExportsQUnitRequire风格的接口。

5.1 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)})})})
})

5.2 TDD

TDD接口提供suite()test()suiteSetup()suiteTeardown()setupteardown()

suite('Array', function() {setup(function() {// ...})suite('#indexOf()', function() {test('should return -1 when not present', function() {assert.equal(-1, [1,2,3].indexOf(4));})})
})

5.3 EXPORTS

EXPORTS接口很像Mocha的前身expresso,键值beforeafterbeforeEachafterEach是特殊用例,对象类型的属性值是测试套件,方法类型的属性值是测试用例:

module.exports = {before: function() {// ...},'Array': {'#indexOf()': {'should return -1 when not present': function() {[1,2,3].indexOf(4).should.equal(-1)}}}
}

5.4 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)
})

5.5 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').asserttestCase('Array', function() {pre(function() {// ...});testCase('#indexOf()', function() {assertions('should return -1 when not present', function() {assert.equal([1,2,3].indexOf(4), -1)})})
})

6. 测试报告

Mocha的测试报告与命令行窗口适配,且当标准输出串口没有关联到打印机时始终禁用ANSI-escape颜色。

6.1 SPEC

这是默认的测试报告。“SPEC”测试报告输出与测试用例一致的嵌套视图。

Mocha.js官方文档翻译 —— 简单、灵活、有趣

spec 测试报告

Mocha.js官方文档翻译 —— 简单、灵活、有趣

带有失败状态的spec 测试报告

6.2 Dot Matrix

Dot Matrix(或者Dot)测试报告使用一串简单字符来表示测试用例,失败的(failing)以红色叹号(!)表示,挂起的(pedding)以蓝色逗号(,)表示,长时的以黄色表示。当你希望最小化输出的时候这就很好。

Mocha.js官方文档翻译 —— 简单、灵活、有趣

dot matrix 测试报告

6.3 NYAN

NYAN测试报告就是你想的那样(一只猫):

Mocha.js官方文档翻译 —— 简单、灵活、有趣

js nyan cat 测试报告

6.4 TAP

TAP测试报告的输出很适合Test-Anything-Protocol的用户。

Mocha.js官方文档翻译 —— 简单、灵活、有趣

test anything protocol

6.5 Landing Strip

landing Strip(landing)测试报告是一个非正式的测试报告器,它用unicode字符模仿了飞机降落的情景。
Mocha.js官方文档翻译 —— 简单、灵活、有趣
landing strip plane 测试报告
Mocha.js官方文档翻译 —— 简单、灵活、有趣
带失败状态的landing strip测试报告

6.6 LIST

list测试报告输出一个测试用例通过或失败的简洁列表,并在底部输出失败用例的详情。

Mocha.js官方文档翻译 —— 简单、灵活、有趣
list测试报告

6.7 PROGRESS

progress测试报告展示一个简单进度条。

Mocha.js官方文档翻译 —— 简单、灵活、有趣
progress bar

6.8 JSON

JSON测试报告在测试完成后输出一个大JSON对象。

Mocha.js官方文档翻译 —— 简单、灵活、有趣
json reporter

6.9 JSON STREAM

JSON stream测试报告输出根据“事件”断行了的JSON,以“start”事件开始,紧跟着测试通过或失败,最后是“end”事件。

Mocha.js官方文档翻译 —— 简单、灵活、有趣
json stream reporter

6.10 MIN

min测试报告仅显示结果摘要,当然也输出失败时的错误信息。当与--watch一起使用时很棒,它会清空你的命令行让测试摘要始终显示在最上方。
Mocha.js官方文档翻译 —— 简单、灵活、有趣
min reporter

6.11 DOC

doc测试报告输出一个层级化的HTML来表示你的测试结果。使用header,footer和一些样式来包裹测试结果,然后你就有了一份惊艳的测试报告文档!
Mocha.js官方文档翻译 —— 简单、灵活、有趣
doc reporter

例如,假定你有下面的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>

存储为文件

$ mocha --recursive -R doc > spec.html

使用此Bash命令,使用Mocha的doc reporter生成了SuperAgent请求库测试文档:

$ mocha --reporter=doc | cat docs/head.html - docs/tail.html > docs/test.html

查看SuperAgent的Makefile 以供参考。

6.12 MARKDOWN

markdown测试报告为你的测试单元能生成一个markdown TOC。当你希望将你的测试结果放在Github的wiki或仓库中时,这会很好用。

$ mocha --recursive -R markdown > spec.md

6.13 HTML

HTML测试报告是当前Mocha唯一支持的浏览器测试报告,类似这样:

Mocha.js官方文档翻译 —— 简单、灵活、有趣
HTML test reporter

6.14 未记载的测试报告

XUnit测试报告也是可以用的。默认地,它输出到console。想直接写入文件,使用--reporter-options output=filename.xml

6.15 第三方的测试报告

Mocha允许自定义第三方的测试报告生成器。浏览wiki获取更多信息。一个例子是TeamCity reporter。

7. 在浏览器中运行Mocha

Mocha可以运行在浏览器中。Mocha的每个释出版本都会有./mocha.js./mocha.css来在浏览器中使用。

7.1 基本用法

首先,使用mocha init命令在指定目录生成初始化文件。

$ mocha init demo

运行上面命令,就会在demo目录下生成index.html文件,以及配套的脚本和样式表。

载入测试脚本,在onload中用mocha.run()运行它们之前,我们调用mocha.setup('bdd')来使用BDD风格的接口。

<html>
<head><meta charset="utf-8"><title>Mocha Tests</title><link href="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css" rel="stylesheet" />
</head>
<body><div id="mocha"></div><script src="https://cdn.rawgit.com/jquery/jquery/2.1.4/dist/jquery.min.js"></script><script src="https://cdn.rawgit.com/Automattic/expect.js/0.3.1/index.js"></script><script src="https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.js"></script><script>mocha.setup('bdd')</script><script src="test.array.js"></script><script src="test.object.js"></script><script src="test.xhr.js"></script><script>mocha.checkLeaks();mocha.globals(['jQuery']);mocha.run();</script>
</body>
</html>

现在,在浏览器里面打开index.html,就可以看到测试脚本的运行结果。

7.2 浏览器专用方法

下面的方法仅在浏览器环境中有效:
mocha.allowUncaught():如果调用,未捕获的错误不会被error handler处理。

7.3 grep

浏览器中也可以使用--grep功能。在你的URL上添加一个请求参数:?grep=api

7.4 浏览器配置

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'
});// Use "tdd" interface, ignore leaks, and force all tests to be asynchronous
mocha.setup({ui: 'tdd',ignoreLeaks: true,asyncOnly: true
});

7.5 浏览器专用选项

下面的选项仅在浏览器环境有效:
noHighlighting:如果设置为true,do not attempt to use syntax highlighting on output test code。

mocha.opts

回到服务器,Mocha会试图加载./test/mocha.opts作为Mocha的配置文件。文件是由命令行参数按行拼接起来的。命令行参数也是有优先级的,例如,假定你有下面的mocha.opt文件:

--require should
--reporter dot
--ui bdd

它会将默认测试报告设置为dot,加载should断言库,并使用BDD风格的接口。然后你可能会继续带参数运行Mocha,这里是开启Growl支持,并将测试报告更换为list:

$ mocha --reporter list --growl

8. test/文件夹

Mocha默认会全局寻找./test/*.js./test/*.coffee,所以你可能需要将你的测试文件放到./test文件夹中

9. 编辑器插件

下面的编辑器插件package可用:

9.1 TextMate

Mocha的TextMate包包含了能够加速测试编写的代码片段。克隆Mocha repo并运行make tm来安装这个包

9.2 JetBrains

JetBrains为它们的IDE套件(IntelliJ IDEA,WebStorm等)提供了一个NodeJS插件,包含了一个Mocha test runner,和一些周边。

Mocha.js官方文档翻译 —— 简单、灵活、有趣

运行中的JetBrains Mocha Runner Plugin

插件名为NodeJS,并且可以通过Preference > Plugins来安装…如果你的许可允许的话。

9.3 Wallaby.js

Wallaby.js是一个持续测试工具,为JetBrains IDE和Visual Studio中的Mocha提供实时的测试覆盖率,不管是运行在node.js还是浏览器的项目。
Mocha.js官方文档翻译 —— 简单、灵活、有趣

运行中的Wallaby.js

9.4 Emacs

Emacs支持通过第三方插件mocha.el来运行Mocha测试。插件可以在MELPA上找到,也可通过M-x package-install mocha来安装。

Mocha.js官方文档翻译 —— 简单、灵活、有趣
运行中的Emacs Mocha Runner

10. Http测试

用mocha测试一个async函数是非常方便的。现在,当我们有了一个koa的Web应用程序时,我们怎么用mocha来自动化测试Web应用程序呢?

一个简单的想法就是在测试前启动koa的app,然后运行async测试,在测试代码中发送http请求,收到响应后检查结果,这样,一个基于http接口的测试就可以自动运行。

我们先创建一个最简单的koa应用,结构如下:

koa-test/
|
+- .vscode/
|  |
|  +- launch.json <-- VSCode 配置文件
|
+- app.js <-- koa app文件
|
+- start.js <-- app启动入口
|
+- test/ <-- 存放所有test
| |
|  +- app-test.js <-- 异步测试
|
+- package.json <-- 项目描述文件
|
+- node_modules/ <-- npm安装的所有依赖包

这个koa应用和前面的koa应用稍有不同的是,app.js只负责创建app实例,并不监听端口:

// app.jsconst Koa = require('koa');const app = new Koa();app.use(async (ctx, next) => {const start = new Date().getTime();await next();const ms = new Date().getTime() - start;console.log(`${ctx.request.method} ${ctx.request.url}: ${ms}ms`);ctx.response.set('X-Response-Time', `${ms}ms`);
});app.use(async (ctx, next) => {var name = ctx.request.query.name || 'world';ctx.response.type = 'text/html';ctx.response.body = `<h1>Hello, ${name}!</h1>`;
});module.exports = app;

start.js负责真正启动应用:

// start.jsconst app = require('./app');app.listen(3000);
console.log('app started at port 3000...');

这样做的目的是便于后面的测试。

紧接着,我们在test目录下创建app-test.js,来测试这个koa应用。

在测试前,我们在package.json中添加devDependencies,除了mocha外,我们还需要一个简单而强大的测试模块supertest

{..."devDependencies": {"mocha": "3.0.2","supertest": "3.0.0"}
}

运行npm install后,我们开始编写测试:

// app-test.jsconstrequest = require('supertest'),app = require('../app');describe('#test koa app', () => {let server = app.listen(9900);describe('#test server', () => {it('#test GET /', async () => {let res = await request(server).get('/').expect('Content-Type', /text\/html/).expect(200, '<h1>Hello, world!</h1>');});it('#test GET /path?name=Bob', async () => {let res = await request(server).get('/path?name=Bob').expect('Content-Type', /text\/html/).expect(200, '<h1>Hello, Bob!</h1>');});});
});

在测试中,我们首先导入supertest模块,然后导入app模块,注意我们已经在app.js中移除了app.listen(3000);语句,所以,这里我们用:

let server = app.listen(9900);

让app实例监听在9900端口上,并且获得返回的server实例。

在测试代码中,我们使用:

let res = await request(server).get('/');

就可以构造一个GET请求,发送给koa的应用,然后获得响应。

可以手动检查响应对象,例如res.body,还可以利用supertest提供的expect()更方便地断言响应的HTTP代码、返回内容和HTTP头。断言HTTP头时可用使用正则表达式。例如,下面的断言:

.expect('Content-Type', /text\/html/)

可用成功匹配到Content-Typetext/htmltext/html; charset=utf-8等值。

当所有测试运行结束后,app实例会自动关闭,无需清理。

利用mocha的异步测试,配合supertest,我们可以用简单的代码编写端到端的HTTP自动化测试。