引子

有群友问到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 等覆盖率数据文件:

实例

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 得到覆盖率报表: 
  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 查看覆盖率报表,我们能看到进步 :) 

后记

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

express, mocha, supertest,istanbul的更多相关文章

  1. nodejs+mocha+supertest+chai进行测试(only demo)

    1.nodejs安装成功 (上一篇:brew install nodejs) 2.mocha安装成功 npm install -g mocha 解释: -g代表global,全局的意思.此处mocha ...

  2. mocha、chai、sinon和istanbul实现100%单元测试覆盖率

    敏捷软件开发中,最重要实践的就是测试驱动开发,在单元测试层面,我们试着实现一个重要的指标就是测试覆盖率.测试覆盖率衡量我们的代码是否已经全部被测试到了. 但是指标本身不是目的,借助测试覆盖率检查,我们 ...

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

    原文地址:http://www.moye.me/2014/12/03/express_coverage/ 引子 有群友问到Express怎么做 单元测试/覆盖率测试,这是上篇所遗漏的,特此补上 Exp ...

  4. node js 调试方法

    1. node-debug tutorial 大家对nodejs调试应该都比较头疼,至少我这个不用IDE写js的人很头疼这个,其实node的生态圈非常好 有非常好的工具和非常潮的开发方式 这里总结了3 ...

  5. npm package.json属性详解

    概述 本文档是自己看官方文档的理解+翻译,内容是package.json配置里边的属性含义.package.json必须是一个严格的json文件,而不仅仅是js里边的一个对象.其中很多属性可以通过np ...

  6. [译]为什么我要离开gulp和grunt转投npm脚本的怀抱

    原文链接:https://medium.freecodecamp.com/why-i-left-gulp-and-grunt-for-npm-scripts-3d6853dd22b8#.n7m1855 ...

  7. (译)package.json详解

    原文链接 概述 本文囊括了所有package.json文件中你需要知道的细节.注意package.json必须是纯JSON的,而不仅仅是一个JavaScript对象字面量.该文件描述的很多行为都受np ...

  8. npm-package.json

    Specifics of npm's package.json handling DESCRIPTION§ This document is all you need to know about wh ...

  9. WePY - 小程序敏捷开发实践丨掘金开发者大会

    声明:内容转载他处,如有侵权,可协商下架 本主题虽然在其它地方讲了很多次,但还是有非常多新内容.因为很多东西正在做或者想要做.本次分享主要分为以下几个部分: WePY 的介绍 WePY 的用户 上面展 ...

随机推荐

  1. hdu 1711 Number Sequence KMP 基础题

    Number Sequence Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  2. html页面中js判断浏览器是否是IE浏览器及IE浏览器版本

    HTML里: HTML代码中,在编写网页代码时,各种浏览器的兼容性是个必须考虑的问题,有些时候无法找到适合所有浏览器的写法,就只能写根据浏览器种类区别的代码,这时就要用到判断代码了.在HTML代码中, ...

  3. 发现一个nginx LUA开发Web App的框架

    nginx是个好东西, nginx的openrtsy发行版本更是个好东西. 今天又发现个好东西 :Moochine MOOCHINE - 一个简单的轻量级的web framework, 基于ngx_O ...

  4. 利用JMX统计远程JAVA进程的CPU和Memory

    http://songzi0206.iteye.com/blog/1541636 ******************** 从JAVA 5开始,JDK提供了一些JVM检测的API,这就是有名的java ...

  5. string 大小写转换

      STL的algorithm库确实给我们提供了这样的便利,使用模板函数transform可以轻松解决这个问题,开发人员只需要提供一个函数对象,例如将char转成大写的toupper函数或者小写的函数 ...

  6. VirtualBox虚拟机和主机之间的通信

    - VirtualBox的NAT网络模式,主机不能访问虚拟机- 端口转发可以访问,但是性能非常差,第一次连接30秒左右- 有条件的还是推荐VmWare

  7. AJax与Jsonp跨域访问

    一.JavaScript的AJax AJAX即"Asynchronous Javascript And XML"(异步JavaScript和XML) 设计AJax使用的一种重要技术 ...

  8. java文件对话框操作

        完毕文件打开与保存   FileDialog : FileDialog fd = new FileDialog(this); fd.setVisible(true);//或fd.show(); ...

  9. 微信小程序5 - 数据驱动界面

    微信小程序不可以使用js直接控制界面元素.而是通过改变 this.data中的属性,同步到界面 这个问题可以克服,带来的就是和H5不一样的编码方式.JS中更多的是改变数据的逻辑,而不是获取某个View ...

  10. vs 常用工具

    工欲善其事,必先利其器,没有好的工具,怎么能高效的开发出高质量的代码呢?本文为 ASP.NET 开发者介绍一些高效实用的工具,包括 SQL 管理,VS插件,内存管理,诊断工具等,涉及开发过程的各个环节 ...