本文详细介绍Node.js中最常用的测试框架Mocha的安装方法及核心用法。涵盖同步与异步测试、断言库使用、Pending测试、Only/Skip控制以及TDD/BDD模式切换,帮助开发者快速掌握Mocha的基本操作与最佳实践。
Mocha简介与安装
在Node.js开发中,Mocha是最广泛使用的测试框架之一。它兼容多种Node.js断言库,同时支持同步和异步测试,并提供多种结果导出方式,甚至可以直接在浏览器环境中运行JavaScript代码测试。
本文示例主要参考官方文档,部分示例根据实际需求或个人理解进行了调整。如需了解更多详情,请访问官方网站:Mocha on Github。
安装步骤:
在成功安装Node.js v0.10及npm后,请执行以下命令进行安装。
# npm install -g mocha
注意:Ubuntu用户需留意apt源中的Node.js版本可能较旧,导致某些模块不兼容,建议从Node.js官网下载源码进行安装。
Mocha第一步:
Mocha基础示例
以下是一个最简单的Mocha示例:
var assert = require("assert");
describe('Array', function(){
describe('#indexOf()', function(){
it('should return -1 when the value is not present', function(){
assert.equal(-1, [1,2,3].indexOf(5));
assert.equal(-1, [1,2,3].indexOf(0));
})
})
});
通过上述代码可以看出,describe函数可以嵌套使用。例如,代码中嵌套的两个describe可以理解为测试人员希望测试Array模块下的#indexOf()子模块。module_name参数可随意命名,但需确保语义清晰易读。 it函数用于编写具体的测试语句,info字符串通常简要描述期望的正确输出结果。当it块内的测试失败时,控制台会打印详细信息,通常从最外层的describe的module_name开始输出(可理解为沿路径、递归链或回调链),最后输出info,表示期望的info内容未被满足。每个it对应一个实际的测试用例。 assert.equal用于判断exp1的结果是否等于exp2,这里采用的是==而非===进行相等判断。例如assert.equal(1, '1')会被视为True。这只是Node.js中assert.js的一种断言形式,下文将介绍同样常用的should.js。 如果exp1和exp2均为字符串且比较出错,控制台会用颜色高亮显示差异部分。
异步测试处理
异步测试
Frist step中的代码显然是同步代码,那么如何处理异步代码呢?方法很简单,在最深处的回调函数中添加done()以表示测试结束。
fs = require('fs');
describe('File', function(){
describe('#readFile()', function(){
it('should read test.ls without error', function(done){
fs.readFile('test.ls', function(err){
if (err) throw err;
done();
});
})
})
})
done()函数 按照瀑布流编程的习惯,命名done表示回调的最深处,即结束嵌套回调函数的编写。但对于回调链而言,done实际上意味着告知Mocha从此处开始测试,并逐层回调回去。
上例代码测试通过,我们尝试将test.ls改为不存在的test.as,便会返回具体的错误位置。
这里可能会有一个疑问:如果我有两个异步函数(两条分叉的回调链),应该在何处添加done()?实际上,此时不应在一个it中包含两个待测试的函数。事实上,一个it中只能调用一次done,若多次调用,Mocha会抛出错误。因此,应类似如下处理:
fs = require('fs');
describe('File', function(){
describe('#readFile()', function(){
it('should read test.ls without error', function(done){
fs.readFile('test.ls', function(err){
if (err) throw err;
done();
});
})
it('should read test.js without error', function(done){
fs.readFile('test.js', function(err){
if (err) throw err;
done();
});
})
})
})
Pending测试用例
Pending(待定)
即省略测试细节,仅保留函数体。一般适用于负责测试框架的人员写好框架让组员实现细节,或测试细节尚未完全正确实现时先注释以避免影响全局测试情况。此时Mocha会默认该测试通过。 其作用类似于Python中的pass语句。
describe('Array', function(){
describe('#indexOf()', function(){
it('should return -1 when the value is not present', function(){
})
})
});
Only与Skip控制
Exclusive && Inclusive(独占与包含)
这很好理解,分别对应only和skip函数。
fs = require('fs');
describe('File', function(){
describe('#readFile()', function(){
it.skip('should read test.ls without error', function(done){
fs.readFile('test.ls', function(err){
if (err) throw err;
done();
});
})
it('should read test.js without error', function(done){
})
})
})
上述代码中只有一个test complete,只有标记为only的会被执行,其他将被忽略。每个函数里只能有一个only。如果是it.skip,则该用例将被忽略。
only和skip共用并无实际意义,因为only的作用会屏蔽skip。
fs = require('fs');
describe('File', function(){
describe('#readFile()', function(){
it.skip('should read test.ls without error', function(done){
fs.readFile('test.as', function(err){
if (err) throw err;
done();
});
})
it('should read test.js without error', function(done){
})
})
})
上述代码尽管test.as不存在,但由于使用了skip,依然会显示test complete。
Before与After生命周期
Before && After(前置与后置)
单元测试中经常用到before和after。Mocha还提供了beforeEach()和afterEach()。 为方便阅读,此处使用livescript表示,!->可理解为function(){}。细节无需细读,只需通过框架了解这几个函数的使用方式即可。
require! assert
require! fs
can = it
describe 'Array', !->
beforeEach !->
console.log 'beforeEach Array'
before !->
console.log 'before Array'
before !->
console.log 'before Array second time'
after !->
console.log 'after Array'
describe '#indexOf()', !->
can 'should return -1 when the value is not present', !->
assert.equal -1, [1,2,3].indexOf 0
can 'should return 1 when the value is not present', !->
describe 'File', !->
beforeEach !->
console.log 'beforeEach file test!'
afterEach !->
console.log 'afterEach File test!'
describe '#readFile()', !->
can 'should read test.ls without error', !(done)->
fs.readFile 'test.ls', !(err)->
if err
throw err
done!
can 'should read test.js without error', !(done)->
fs.readFile 'test.js', !(err)->
if err
throw err
done!
由结果可知(after的使用与before同理),
beforeEach会对当前describe下的所有子case生效。 before和after的代码没有特殊顺序要求。 同一个describe下可以有多个before,执行顺序与代码顺序相同。 同一个describe下的执行顺序为before, beforeEach, afterEach, after。 当一个it有多个before的时候,执行顺序从最外围的describe的before开始,其余同理。
TDD模式切换
Test Driven Develop (TDD,测试驱动开发)
Mocha默认的模式是Behavior Driven Develop (BDD,行为驱动开发)。若要执行TDD测试,需添加参数,例如:
mocha -u tdd test.js
前文所讲的describe, it, before, after等都属于BDD的范畴。对于TDD,我们使用suite, test, setup, teardown。样例代码如下:
suite 'Array', !->
setup !->
console.log 'setup'
teardown !->
console.log 'teardown'
suite '#indexOf()', !->
test 'should return -1 when not present', !->
assert.equal -1, [1,2,3].indexOf 4