首页 > 代码库 > [Node.js] Express的测试覆盖率

[Node.js] Express的测试覆盖率

 

引子

有群友问到Express怎么做 单元测试/覆盖率测试,这是上篇所遗漏的,特此补上

Express Web测试

做 Express Web 测试首先要面对的问题是在哪端进行测试:

  • 客户端的请求响应测试是黑盒,需要预启动站点,且无法附加覆盖率测试
  • 服务端的单元测试需要 Mock ,可附加覆盖率测试

我们需要对Express的路由做覆盖率测试,显然,我们会选择在服务端进行测试。这意味着:每个case需要访问的express application 不是这样预先启动的:

var express = require(‘express‘);var app = express();//some router code...app.listen(3000);

我们需要一个工具能创建启动express application,并 Mock 对它的请求,只有这样,测试框架才能检测到路由方法内部代码执行的路径和覆盖率。

这里,我们引入supertest 做为 mock 工具。

SuperTest

SuperTest 是TJ大神的又一款作品:基于SuperAgent ,提供对HTTP测试的高度抽象。所谓高度抽象的意思是:能嵌入各类测试框架,提供语义良好的断言。

来看段 SuperTest结合 Mocha的代码:

var app = require(‘../app‘);var request = require(‘supertest‘);describe(‘router testing‘, function () {    it(‘site root response‘, function (done) {        request(app)            .get(‘/‘)            .expect(‘Content-Type‘, ‘text/html; charset=utf-8‘)            .expect(200)            .end(function(err, res){                if (err) throw err;                done();            });    });

简单易懂,重点是它驱动了express。

测试覆盖率

代码覆盖(Code coverage)是软件测试中的一种度量,描述程式中源代码被测试的比例和程度,所得比例称为代码覆盖率。

以下是几个覆盖率指标:

  • 函数覆盖率(Function coverage):调用到程式中的每一个Function吗?
  • 行覆盖率(Line coverage):执行到程序中的每一行了吗?
  • 语句覆盖率(Statement coverage):若用控制流图表示程序,执行到控制流图中的每一个节点了吗?
  • 分支覆盖率(Branches coverage):若用控制流图表示程式,执行到控制流图中的每一条边吗?例如控制结构中所有IF指令都有执行到逻辑运算式成立及不成立的情形吗?
  • 条件覆盖率(Condition coverage):也称为谓词覆盖(predicate coverage),每一个逻辑运算式中的每一个条件(无法再分解的逻辑运算式)是否都有执行到成立及不成立的情形吗?

对指标的偏好可说是见仁见智,比如大名鼎鼎的 coveralls.io 就以行覆盖率(Line coverage) 作为给项目颁发badge的首选指标。

我们需要的,是一个能根据测试用例得出覆盖率指标的工具:

Istanbul

istanbul 就是这样一个工具,能产生 Statements/Lines/Functions/Branches 等指标报表,并以各种格式导出。

值得称道的是,istanbul 能和 Mocha 很好的集成,如:把测试用例统一放置在 /test下,要对它们进行测试并生成覆盖率报表,可以在 package.json 中添加这样的配置:

"scripts": {    "test": "mocha --recursive --timeout 500000 test/ --bail",    "test-cov": "node node_modules/istanbul-harmony/lib/cli.js cover ./node_modules/mocha/bin/_mocha  -- --timeout 500000 --recursive test/ --bail"}

只需要进行测试时,在项目目录下使用命令:

npm test

需要进行测试并追加覆盖率报表时,在项目目录下使用命令:

npm run-script test-cov

在测试部分完成后,会得到如下报表信息(在项目 /coverage 目录下,会生成lcov.info 等覆盖率数据文件:

istanbul_coverage

实例

mock 工具有了, 测试框架和覆盖率工具也有了,就差实战了。下面举个粟子看看怎么做 Express 的覆盖率测试:

  1. 全局安装 mocha ,istanbul 及 express
    npm install -g mocha
    npm install -g istanbul
    npm install -g express
  2. 生成一个express 站点:
    express -e express-coverage
  3. 修改package.json如下,并使用npm install 安装需要的包:
    {  "name": "express-coverage",  "version": "0.0.1",  "scripts": {    "test": "mocha test/ --bail",    "test-cov": "node node_modules/istanbul-harmony/lib/cli.js cover ./node_modules/mocha/bin/_mocha  -- test/"  },  "dependencies": {    "express": "~4.9.0",    "body-parser": "~1.8.1",    "cookie-parser": "~1.3.3",    "morgan": "~1.3.0",    "serve-favicon": "~2.1.3",    "debug": "~2.0.0",    "ejs": "~0.8.5",    "istanbul-harmony": "*",    "should": "*",    "mocha": "*",    "mocha-lcov-reporter": "*",    "supertest" : "*"  }}
  4. 把自带的routes/index.js,、bin/www 删除;改写routes/users.js:
    var express = require(‘express‘);var router = express.Router();router.get(‘/‘, function (req, res) {    var msg = ‘no user‘;    res.send(msg);});router.get(‘/:id‘, function (req, res) {    var msg = ‘user: ‘ + req.params.id;    res.send(msg);});module.exports = router;
  5. 在项目下新建一个test目录,放置一个 router.js,并编写用例:
    var should = require(‘should‘);var app = require(‘../app‘);var request = require(‘supertest‘);describe(‘router testing‘, function () {    it(‘users have not id response‘, function (done) {        request(app)            .get(‘/users‘)            .expect(‘Content-Type‘, ‘text/html; charset=utf-8‘)            .expect(200)            .end(function(err, res){                if (err) throw err;                should.exist(res.text);                done();            });    });    it(‘users have id response‘, function (done) {        request(app)            .get(‘/users/1/‘)            .expect(‘Content-Type‘, ‘text/html; charset=utf-8‘)            .expect(200)            .end(function(err, res){                if (err) throw err;                should.exists(res.text);                done();            });    });});
  6. 输入命令npm run-script test-co 得到覆盖率报表:
    testing_coverage_first
  7. 指标有点低是不是,因为app里有分支和代码是用例没跑到的: 404和500处理代码(这些是express-generator的生成代码:
    app.use(function(req, res, next) {    var err = new Error(‘Not Found‘);    err.status = 404;    next(err);});app.use(function(err, req, res, next) {    res.status(err.status || 500);    res.render(‘error‘, {        message: err.message,        error: {}    });});
  8. 在 router.js 原有场景中,添加一个用例,加上对404代码行的触发:
    it(‘404 response‘, function (done) {    request(app)        .get(‘/non/‘)        .expect(404)        .end(function(err, res){            if (err) throw err;            done();        });});
  9. 再次输入命令npm run-script test-co 查看覆盖率报表,我们能看到进步 :)
    testing_coverage_second

后记

badge找到合适的 Mock工具和测试框架并进行整合,Web测试及覆盖率报表获取的思路大抵如此。关于测试框架的各种参数组合和花样玩法,还有很多有意思的功能(比如和 Travis-CI、Coveralls.io 等公共服务集成,在仓库上展示项目状态徽章),本文不再赘述,有兴趣的可加node学习交流群一起探讨。

 

更多文章请移步我的blog新地址: http://www.moye.me/  

[Node.js] Express的测试覆盖率