NodeJs学习笔记,四---单元测试

sailsjs框架用了一段时间了,感觉如果功能复杂了,非常难以处理,想用一下单元测试,看是否能解决问题。

sailsjs的官方文档使用的是mocha,我搜索了一些资料,主要参考了朴灵的《深入浅出nodejs》的单元测试一章、sailsjs的官方文档以及github上的一个例子https://github.com/bredikhin/sailsjs-mocha-testing-barrels-fixtures-example

1)系统环境如下

Windows 8.1 X64

nodejs 0.12.1

sailsjs 0.11.0

2)安装过程

需要安装mocha和should,mocha是测试框架,should是一个断言,也可以使用原生的assert,supertest是用来测试http请求的,这里用来测试controller

npm install mocha -g

npm install should

npm install supertest

其中mocha的主页为http://mochajs.org/,should的主页是https://www.npmjs.com/package/should,可以从这些网站找到相关的文档。

mocha是安装为全局命令的,可以直接在命令行运行mocha,其参数较多,可以用mocha --help查看一下参数。

3)目录和文件准备

./myApp
├── api
├── assets
├── ...
├── test
│  ├── unit
│  │  ├── controllers
│  │  │  └── UserController.test.js
│  │  ├── models
│  │  │  └── User.test.js
│  │  └── ...
│  ├── fixtures
│  ├── ...
│  ├── bootstrap.test.js
│  └── mocha.opts
└── views
首先在根目录下建立test目录,然后一次建立test\unit\controllers和test\unit\models目录。

然后在test下建立文件bootstrap.test.js,这个文件是用来启动sailsjs程序的,也可以说是配置环境的,内容如下

var Sails = require('sails'),

sails;

before(function (done) {

Sails.lift({

log : {

level : 'error' //指定错误级别,避免出现调试输出,这主要是用来调试controller

}

}, function (err, server) {

sails = server;

if (err)

return done(err);

// here you can load fixtures, etc.

done(err, sails);

});

});

after(function (done) {

var done_called = false;

Sails.lower(function () {

if (!done_called) {

done_called = true;

setTimeout(function () {

sails.log.debug("inside app.lower, callback not called yet. calling.");

done();

}, 1000);

} else {

sails.log.debug("inside app.lower, callback already called.");

}

});

});

需要注意的是,after()和官方文档的不一致,因为会出现done()调用多次的问题,所以我参考了网上的一个资源,增加了等待。

4)测试控制器(controller)

在test\unit\controllers下建立UserController.test.js,内容如下
var request = require('supertest');
var should = require('should');
describe('UsersController', function () {
describe('#login()', function () {
it('should get 1', function (done) {
request(sails.hooks.http.app)
.get('/user/login?login_code=12345678901&user_password=111111')
.end(function (err, results) {
should(results.res.body.result).be.exactly(1);
done();
});
});
});
});
这里用的是BDD的风格,我比较喜欢这种方式,也可以采用TDD的风格,感觉都差不多
上面的代码也很简单,对/user/login?login_code=12345678901&user_password=111111发起请求,根据返回内容进行判断,这里要注意一下,返回的信息要多于通常的返回,比如我的返回是类似于{result: 1, desc:'登录成功'}之类的,但是supertest返回的要多得多,所以用result.res.body.result才能得到result值,也可以将信息打印出来看一下,不过需要注意的是因为前面设置了log:{level: 'error'},可以看不到输出,可以将error改成info,再试一下。

5)运行测试

mocha -t 10000 test/bootstrap.test.js test/unit/**/*.test.js

也可以在package.json中的scripts下增加npm命令,如下

"scripts": {

"start": "node app.js",

"debug": "node debug app.js",

"test": "mocha -t 10000 test/bootstrap.test.js test/unit/**/*.test.js"

},

这样就可以直接运行npm test执行测试了。

需要说明一下, -t 10000代表每个测试的执行时间增加到10秒,默认是2秒,在我的系统上无法返回,会出现错误,可能我的机器太慢了吧。

如果用户名密码正确,则返回

1 passing (7s)

如果出现错误,则返回

0 passing (6s)

1 failing

1) UsersController #login() should get 1:

Uncaught AssertionError: expected 0 to be 1

+ expected - actual

-0

+1

at Test.<anonymous> (D:\work\nodejs\KangfuMobile\test\unit\controllers\Use rController.test.js:13:40)

at net.js:1392:10

所以用来测试controller是非常简单的,

6)测试逻辑,在test\unit\models建立User.test.js,内容为

var should = require('should');

var bcrypt = require('bcrypt');

describe('用户表', function () {

describe('查找用户信息', function () {

it('应该检查用户表是否没有这个用户了', function (done) {

User.find({

user_code : '12345678901'

}).exec(function (err, results) {

should(results.length).be.exactly(1);

done();

});

});

});

describe('查找用户名密码', function () {

it('应该检查密码是否录入错误,或者密码加密系统出现错误', function (done) {

User.findOne({

user_code : '12345678901'

}).exec(function (err, user) {

var user_password = '111111';

var user_password_o = user.user_password;

user_password_o = "$2a$" + user_password_o.substr(4);

bcrypt.compare(user_password, user_password_o, function (err, comp) {

should(comp).be.exactly(true);

done();

});

});

});

});

});

上面有两个测试,一个是查看是否存在且只有一个用户名为“12345678901”的用户,第二个是查看其密码是否正确。

运行测试命令,得到如下结果,如果没有用户,则会出现下面的错误:

2 passing (6s)

3 failing

1) UsersController #login() should get 1:

Uncaught AssertionError: expected 0 to be 1

+ expected - actual

-0

+1

at Test.<anonymous> (D:\work\nodejs\KangfuMobile\test\unit\controllers\Use rController.test.js:13:40)

at net.js:1392:10

2) 用户表 查找用户信息 应该检查用户表是否没有这个用户了:

Uncaught AssertionError: expected 0 to be 1

+ expected - actual

-0

+1

at D:\work\nodejs\KangfuMobile\test\unit\models\Users.test.js:10:31

at readableAddChunk (_stream_readable.js:163:16)

at Socket.Readable.push (_stream_readable.js:126:10)

at TCP.onread (net.js:529:20)

3) 用户表 查找用户名密码 应该检查密码是否录入错误,或者密码加密系统出现错误:

Uncaught TypeError: Cannot read property 'user_password' of undefined

at D:\work\nodejs\KangfuMobile\test\unit\models\Users.test.js:21:31

at readableAddChunk (_stream_readable.js:163:16)

at Socket.Readable.push (_stream_readable.js:126:10)

at TCP.onread (net.js:529:20)

通过上面就可以查看是否出现了错误,并检查可能的问题,如果修改正确了,就会

5 passing (6s)

7)基本来说,单元测试的基本内容就清楚了,不过想要更好的使用,还需要学会如何使用断言,比如should,还需要学习supertest的语法,mocha的BDD风格倒是非常简单,就是

describe('测试内容',function(){

describe('测试内容1', function(){

it('需要注意什么', function(done){

//做测试

done();

})

})

})

如果用过async框架,就会对done()的使用非常熟悉了,这主要是用来处理异步问题的,等到done()返回才算测试结束了。

should的使用非常简单,可以参考官方文档,也可以看看牛人写的例子:https://github.com/shouldjs/examples