• express, mocha, supertest,istanbul


    引子

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

    Express Web测试

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

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

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

    1
    2
    3
    4
    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的代码:

    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。

    测试覆盖率

    代码覆盖(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 中添加这样的配置:

    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学习交流群一起探讨。

  • 相关阅读:
    Hadoop添加节点的方法
    CSS中如何实现DIV居中
    Python新手入门教程,从环境准备到掌握基本编程
    八、使用pull解析器操作xml文件
    九、使用SharedPreferences进行数据存储
    十八、发送xml数据给服务器
    四、对应用进行单元测试
    二、短信发送器
    一、电话拨号器
    十四、ContentProvider往通讯录添加联系人和获取联系人
  • 原文地址:https://www.cnblogs.com/SZLLQ2000/p/5368912.html
Copyright © 2020-2023  润新知