原文地址:http://www.moye.me/2014/12/03/express_coverage/

引子

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

实例

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

后记

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

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

[Node.js] Express的测试覆盖率的更多相关文章

  1. Windows下Node.js+Express+WebSocket 安装配置

    Linux参考: Linux安装Node.js 使用Express搭建Web服务器 Node.js是一个Javascript运行环境(runtime).实际上它是对Google V8引擎进行了封装.V ...

  2. node.js + express 初体验【hello world】

    [node.js]  一个神奇的XX 呵呵 :) 不知道怎么形容他才好! [express] 是node.js 开发web应用程序的框架 开发环境:XP 大家共同进步吧 :) 一:前期准备: 1:下载 ...

  3. Node.js+Express+MongoDB数据库实现网页注册登入功能

    通过 Node.js + Express + MongoDB 实现网页注册账号 和 登入账号的功能 项目准备: 1: 事先准备好项目的页面 (首页页面 index.html)(登入页面 login.h ...

  4. node.js+express+mongoose实现用户增删查改案例

    node.js+express+mongodb对用户进行增删查改 一.用到的相关技术 使用 Node.js 的 express 框架搭建web服务 使用 express 中间件 body-parse ...

  5. Node.js Express 框架学习

    转载:http://JavaScript.ruanyifeng.com/nodejs/express.html#toc0 感觉很牛的样子,不过觉得对初学者没太大用,里面很多例子用的api都没有详细的说 ...

  6. Node.js Express 框架

    Node.js Express 框架 Express 简介 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP ...

  7. Nodejs学习笔记(六)--- Node.js + Express 构建网站预备知识

    目录 前言 新建express项目并自定义路由规则 如何提取页面中的公共部分? 如何提交表单并接收参数? GET 方式 POST 方式 如何字符串加密? 如何使用session? 如何使用cookie ...

  8. React+Node.js+Express+mongoskin+MongoDB

    首发:个人博客,更新&纠错&回复 采用React + Node.js + Express + mongoskin + MongoDB技术开发的一个示例,演示地址在这里,项目源码在这里. ...

  9. Node.js Express框架

    Express 介绍 Express是一个最小的,灵活的Node.js Web应用程序框架,它提供了一套强大的功能来开发Web和移动应用程序. 它有助于基于Node Web应用程序的快速开发.下面是一 ...

随机推荐

  1. asynchttpClient框架关于多文件批量上传的问题,改用xUtil

    RequestParams params = new RequestParams(); params.add("ordernum",ordernum); params.add(&q ...

  2. windows xp/7命令提示符强制结束指定进程

    开始----“运行 ”输入cmd ,然后在命令提示符下输入tasklist,出现如下列表: Image Name                     PID Session Name        ...

  3. 由1433端口入侵,浅谈sqlserver安全 (转)

    前几日笔者在家里的PC上安装了Windows7旗舰版的操作系统,顺便搭了sqlserver2008和vs2010的开发环境,本打算业余时 间可以方便开发.学习.可是不尽人意啊!用了不到两天,居然突然出 ...

  4. quartzScheduler_Worker-1] but has failed to stop it. This is very likely to create a memory leak解决

    01-Jul-2016 07:24:20.218 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 80 ...

  5. Halcon标定步骤

    Halcon标定步骤 1.设置相机内部参数的初始值 StartCamPar := [0.016,0,0.0000074,0.0000074,326,247,652,494]set_calib_data ...

  6. 记一次苦逼的SQL查询优化

    最近在维护公司项目时,需要加载某页面,总共加载也就4000多条数据,竟然需要35秒钟,要是数据增长到40000条,我估计好几分钟都搞不定.卧槽,要我是用户的话估计受不了,趁闲着没事,就想把它优化一下, ...

  7. leveldb - menifest文件格式

    MANIFEST文件是Level DB的元信息文件,它里面的格式是leveldb的Log格式,一个menifest是一个record: void VersionEdit::EncodeTo(std:: ...

  8. 题目:打印出所有的 "水仙花数 ",所谓 "水仙花数 "是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个 "水仙花 数 ",因为153=1的三次方+5的三次方+3的三次方。

    题目:打印出所有的 "水仙花数 ",所谓 "水仙花数 "是指一个三位数,其各位数字立方和等于该数本身.例如:153是一个 "水仙花 数 ", ...

  9. dede教程之后台登录是自动跳出解决方法

    有时也不知道什么原因,登录后台时输入全部正确点确认按钮时却会自动跳出.必须输入http://你的域名/dede/login.php才可以登录.通过尝试最终解决了问题,下面分享出来: 1.打开根目录da ...

  10. 多个App间传递数据

    平台:Android两个App:A,B:需求:在A中点击一个按钮后,启动B并把数据从A传递到B: 代码: App A: MainActivity.java中添加: Button btn2 = (But ...