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

3,018 阅读6分钟

本次翻译时间为2016年9月底,目前Mocha的版本为3.1.0。 官方文档地址: mochajs.org/ —— Awey

1. 简介

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

2. 支持者

觉得Mocha很有帮助?成为支持者并以每月捐赠的形式支持Mocha。

3. 赞助商

公司正在使用Mocha?询问你的上司或者市场部看看他们是否捐赠过Mocha。(如果捐赠过)你们公司的LOGO会展示在npmjs.com和我们的github仓库

4. 特性

  • 支持浏览器
  • 支持简单异步,包括promise
  • 测试覆盖率报告
  • 支持字符串比较
  • 提供JavaScript API来运行测试
  • 为持续集成等需求提供适当的退出状态
  • non-ttys 自动检测和禁用颜色
  • 异步测试超时
  • ... ...(后略)

5. 目录

略。 本次翻译并未完全按照官方文档的文档结构进行。

6. 开始使用

6.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进行引用。

6.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))
    })
  })
})

回到命令行:

Array
  #indexOf()
    √ 未找到值时应当返回-1

1 passing (9ms)

6.3 断言

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

6.4 异步代码

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

describe('User', function () {
  describe(#'save()', function () {
    it('应当正常保存', function () {
      var user = new User('Luna')
      user.save(function (err) {
        if (err) done(err)
        else done()
      })
    })
  })
})

简便起见,done()函数接受一个error参数,所以上面的代码可以这么写:

describe('User', function () {
  describe(#'save()', function () {
    it('应当正常保存', function () {
      var user = new User('Luna')
      user.save(done)
  })
})
Promise

有时,与其使用done()回调函数,你会想在你的异步代码中返回一个Promise[3],当你正在测试的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()会被忽略(而不会报错)。

6.5 同步代码

当测试同步代码的时候,省略回调函数[4],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)
    })
  })
})

6.6 箭头函数

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

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

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

6.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())既可以是同步的也可以是异步的,(这一点上)它们的行为与普通的测试用例非常相似[5]

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[6],这会在全局注入一个特殊的回调函数run()

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

  run()
}, 5000)

6.8 挂起测试(Pending Tests)

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

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

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

6.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 () {
      // 这个测试用例不会运行
    })
  })
})

但测试会存在优先级[8]

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

    it('should return the index when present', function() {
      // 这个测试用例不会运行
    })
  })
})

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

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

6.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()
  }
})

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

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

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

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

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

6.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
  })
})

6.12 动态生成测试

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

例如:

var assert = require('chai').assert

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

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

$ mocha

  add()
    ✓ correctly adds 2 args
    ✓ correctly adds 3 args
    ✓ correctly adds 4 args

6.13 测试耗时

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

测试耗时

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

describe('something slow', function() {
  this.slow(10000)

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

6.14 测试超时

套件级别

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

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()传递一个大于最大延迟值的参数会让超时限制失效

6.15 差异比较

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

“string”差异

7. 命令行

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() {
      // ...
    })
  })
})

8.接口[13]

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

8.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)
      })
    })
  })
})

8.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));
    })
  })
})

8.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)
      }
    }
  }
}

8.4 QUNIT

QUnit接口与QUnit的“扁平化”外观相匹配[14],测试套件只需要简单地在测试用例之前定义就行。和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)
})

8.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').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)
    })
  })
})

9. 测试报告

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

9.1 SPEC

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

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

9.2 Dot Matrix

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

dot matrix 测试报告

9.3 NYAN

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

js nyan cat 测试报告

9.4 TAP

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

test anything protocol

9.5 Landing Strip

landing Strip(landing)测试报告是一个非正式的测试报告器,它用unicode字符模仿了飞机降落的情景。

landing strip plane 测试报告

带失败状态的landing strip测试报告

9.6 LIST

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

list测试报告

9.7 PROGRESS

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

progress bar

9.8 JSON

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

json reporter

9.9 JSON STREAM

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

json stream reporter

9.10 MIN

min测试报告仅显示结果摘要,当然也输出失败时的错误信息。当与--watch一起使用时很棒,它会清空你的命令行让测试摘要始终显示在最上方。

min reporter

9.11 DOC

doc测试报告输出一个层级化的HTML来表示你的测试结果。使用header,footer和一些样式来包裹测试结果,然后你就有了一份惊艳的测试报告文档!

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>

The SuperAgent request library test documentation was generated with Mocha’s doc reporter using this simple make target:

test-docs:
	$(MAKE) test REPORTER=doc \
		| cat docs/head.html - docs/tail.html \
		> docs/test.html

View the entire Makefile for reference.

9.12 MARKDOWN

markdown测试报告为你的测试讨价能生成一个markdown TOC。当你希望将你的测试结果放在Github的wiki或仓库中时,这会很好用。这是一个例子的链接测试输出

9.13 HTML

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

HTML test reporter

9.14 未记载的测试报告

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

9.15 第三方的测试报告

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

10. 在浏览器中运行Mocha

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

10.1 浏览器专用方法

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

一个典型的设置看起来可能像下面这样,在载入测试脚本,在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>

10.2 grep

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

10.3 浏览器配置

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

10.4 浏览器专用选项

下面的选项仅在浏览器环境有效: 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

11. test/文件夹

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

12. 编辑器插件

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

12.1 TextMate

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

12.2 JetBrains

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

运行中的JetBrains Mocha Runner Plugin

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

12.3 Wallaby.js

Wallaby.js是一个持续测试工具,为JetBrains IDE和Visual Studio中的Mocha提供实时的测试覆盖率,不管是运行在node.js还是浏览器的项目。

运行中的Wallaby.js

12.4 Emacs

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

运行中的Emacs Mocha Runner

13. 案例

真实案例代码:

14. 测试Mocha

运行Mocha本身的测试,你可能需要GUN Make或者其它兼容的环境;Cygwin应该就可以。

$ cd /path/to/mocha
$ npm install
$ npm test

使用不同的测试报告:

$ REPORTER=nyan npm test

15. 更多信息

除了在Gitter上与我们交谈,也可以去GitHub上的Mocha Wiki获取诸如 using spies、mocking和shared behaviours等更多信息。加入Google Group进行讨论。查看example/tests.html获取Mocha运行实例。查看source获取 JavaScript API。


  1. 译者注:这里的意思是,只要这个断言库在遇到测试不通过时会抛出异常(throws an Error),它就可以使用在Mocha中。这意味着即使你不使用任何断言库,完全自己实现测试代码,只要在遇到测试部不通过时你抛出异常,也是可以的,不过通常没有人愿意这么费力不讨好 ↩︎

  2. 译者注:在it()函数的第二个参数,也就是it的回调函数中,添加一个参数,这个参数也是一个回调函数,命名随意,通常都会命名为done,然后在异步代码运行完毕后调用它即可 ↩︎

  3. 译者注:这是为了避开无穷的回调漩涡 ↩︎

  4. 译者注:这里指的是省略done()这个回调函数 ↩︎

  5. 译者注:还记得前文提到的done()回调函数么,在这些钩子中处理异步代码与在测试用例中处理异步代码是一样的 ↩︎

  6. 译者注:这是指在命令行运行 mocha 命令时带上 --delay参数,下文的setTimeout仅做演示用,并非真实的异步操作 ↩︎

  7. 译者注:这里的意思是,可以使用挂起的测试用例来占个位置,先写上测试用例,但可能由于某些原因,比如被测代码还未实现,这个测试用例的内容将在稍后编写,这样的测试用例既不会pass也不会fail,而是处于pendding状态 ↩︎

  8. 译者注:这里指的是嵌套的only()会存在优先级 ↩︎

  9. 译者注:这里所谓的“什么也没做”指的是当测试环境不正确时,测试代码什么也没做,只是简单的跳过了 ↩︎

  10. 译者注:这里的“什么也不做”与前文的“什么也不做”不是一个意思,这里的意思是既不写断言也不跳过,只是简单地空在那里(简单地空在那里这个测试的状态也会是passing) ↩︎

  11. 译者注:这种方式指的是,判断测试环境是否准本好并使用this.skip()来跳过测试 ↩︎

  12. 译者注:“覆盖这个值”指的是在嵌套的测试套件或者测试用例中再次通过this.timeout()来进行超时限制从而覆盖父级套件的设置 ↩︎

  13. 译者注:指的是接口风格 ↩︎

  14. 译者注:这里指的是编程风格扁平化,即没有多层嵌套 ↩︎