express, mocha, supertest,istanbul

引子

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

Express Web测试

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

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

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

1
2
3
4
var  express = require( 'express' );
var  app = express();
//some router code...
app.listen(3000);

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

这里,咱们引入supertest 作为 mock 工具。node

SuperTest

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

来看段 SuperTest结合 Mocha的代码:github

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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。shell

测试覆盖率

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

如下是几个覆盖率指标:npm

  • 函数覆盖率(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 中添加这样的配置:

1
2
3
4
"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"
}

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

1
npm test

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

1
npm run-script test -cov

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

istanbul_coverage

实例

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

  1. 全局安装 mocha ,istanbul 及 express
    1
    npm install  -g mocha
    1
    npm install  -g istanbul
    1
    npm install  -g express
  2. 生成一个express 站点:
    1
    express -e express-coverage
  3. 修改package.json以下,并使用npm install 安装须要的包:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
       "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:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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,并编写用例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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-cov 获得覆盖率报表: 
    testing_coverage_first
  7. 指标有点低是否是,由于app里有分支和代码是用例没跑到的: 404和500处理代码(这些是express-generator的生成代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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代码行的触发:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    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-cov 查看覆盖率报表,咱们能看到进步 :) 
    testing_coverage_second

后记

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

相关文章
相关标签/搜索