前言
单元测试对于软件工程是必不可少的。
为什么说对于软件工程?因为一般说到工程,必然是由团队进行开发的,可能会有互相依赖的调用关系,单元测试可以很好的反映模块的功能完备和健壮水平。
为什么说必不可少,单元测试一般就是用一段代码来检测项目的代码是否符合预期的设计。由于是代码,所以可以毫不费力的反复运行。代替程序员做一些必不可少的测试。
静下心来体会单元测试的好处,慢慢的,你就会爱上她。
依赖模块
单元测试可以有很多种实施方案,下面是我公司采用的一种。
* mocha 单元测试框架
* chai 断言库
* sinon 可以模拟函数
* q promise库,配合sinon模拟返回promise的函数
* proxyquire 代理node模块中的require
开始
1. mocha
mocha 测试框架,支持定义一下两种测试对象
* suite
describe('Suite description',function(){
//specs here
});
- spec
it('Spec description',function(){});
实际写的时候,可以只定义好测试的描述(function留空),然后最后再补齐测试脚本。如下
it('Suite test');
spec是具体的测试用例,suite相当于spec的集合。suite也是可以嵌套的。
mocha支持几种有用的语法,
skip 跳过某个spec或者suite
only 单独测试某个spec或者suite
it.skip
describe.only
被跳过的测试会在测试状态中显示为 pending
最后要注意一点,项目中使用到的 module 一定要写到 package.json 的 dependencies 或者 devDependencies 中。否则,项目在其他环境build的时候,会由于没有这些 module 而发生错误。
2. chai
chai是一个断言库,node本身也有个断言模块,但是功能比较弱,语法也比较贫乏。 chai支持 tdd 和 bdd 两种风格,都比较贴近自然语言(意思就是好学好记)。
tdd风格用法:
首先,要require chai,并且到处它的 tdd风格的接口
var chai = require("chai"),
should = chai.should(),
然后就可以在spec中使用了
it('Try should',function(){
var a = {};
a.should.be.a('object');
});
run一下test文件,我这里文件名叫 me.test.js
mocha me.test.js
不出意外,你的测试pass了。
更多用法请见官网。
3 sinon
单元测试的时候,一般要求独立的测试某个函数的功能,这就要求我们必须模拟这个函数所依赖的资源,比如远端restful service,数据库,文件系统,等等。 sinon提供了一系列方法,可以用来模拟函数。(事实上,sinon提供了很多方法,可惜没有时间学完)
sinon的核心概念是stub,可以理解为凭空造一个函数
var saveUser = sinon.stub();
很显然,我想模拟一个saveUser函数,原本它应该向数据库中insert数据,然后返回insert成功的数据,这里我们模拟它的行为。
saveUser.withArgs({name:'numa'}).returns({id:1,name:'numa'});
withArgs 规定了输入参数,然后紧接着,调用returns,指定对应的输出。
所谓函数,不就是输入->输出吗?
好了,模拟成功了。你可以调用一下这个saveUser,然后看看它是否会返回指定的值。
4 q
q 是一个promise库。
当我们想要用 sinon 模拟返回promise的函数时,q就派上用场了。
最简单的方式, saveUser 会返回promise,我们需要模拟它。
var saveUser = sinon.stub();
saveUser.withArgs({name:'numa').returns(Q.promise(function(resolve){
resolve({id:1,name:'numa'});
}));
saveUser({name:'numa').then(function(res){
console.log(res);
});
5 proxyquire
proxyquire 是搭配 sinon 使用的,sinon 负责模拟函数,proxyquire 负责将待测试单元所依赖的模块替换成模拟的模块。
比如,我们想要将 usermgmt 模块中依赖的 db 模块替换成 sinon 模拟出来的 dbfake 函数。可以这样做:
var usermgmt = proxyquire('./usermgmt',{
'db':dbfake
});
需要注意的是,上面的db表示的是 usermgmt 模块对于 db 模块的依赖路径。也就是说, usermgmt 模块中对于 db 模块也是这样依赖的。
var db = require('db');
如果 usermgmt 中的依赖路径是这样:
var db = require('../lib/db');
那么,使用 proxyquire 替换依赖的时候,就应该使用上面的路径:
var usermgmt = proxyquire('./usermgmt',{
'../lib/db':dbfake
});
这个路径和测试脚本与原 db 模块的相对路径关系无关。
后记
以上是对 nodejs 单元测试使用到的库的简单归纳,既不详细,亦不全面。关于更详细的说明,请移步官网(文章开始所标注的链接),或者,关注本博客关于单元测试的后续文章。