原文地址: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. SqlServer将没有log文件的数据库文件附加到服务器中

    今天搞了一件很让我不爽的事情,一不小心把一个40多G的数据库日志文件删除,而且在删除之前我又搞了个日志进去,死活附加不了到服务器上去一直提示多个日志不能自动创建,白白浪费了我一个晚上的时间,后来不断的 ...

  2. iOS:OC Lib:MagicalRecord

    # MagicalRecord 2.1 ## 前言 CoreData是iOS开发中经常使用的数据持久化的技术.但其操作过程稍微繁琐,即使你只是实现简单的存取,不涉及请求优化,也要进行许多配置工作,代码 ...

  3. 【Linux】文件特殊权限 SUID/SGID/Sticky Bit

    linux中除了常见的读(r).写(w).执行(x)权限以外,还有3个特殊的权限,分别是setuid.setgid和stick bit 1.setuid.setgid 先看个实例,查看你的/usr/b ...

  4. 一个类有两个方法,其中一个是同步的,另一个是非同步的; 现在又两个线程A和B,请问:当线程A访问此类的同步方法时,线程B是否能访问此类的非同步方法?

    一个类有两个方法,其中一个是同步的,另一个是非同步的:现在又两个线程A和B,请问:当线程A访问此类的同步方法时,线程B是否能访问此类的非同步方法? 答案:可以 验证 package com.my.te ...

  5. nodejs 的安全

    1.connect中间件csrf 原理:在express框架中csrf 是通过connect 模块的中间件来解决的.其原理是在前端构造一个隐藏的表单域“_csrf” ,后端生成一个值,作为该表单域,然 ...

  6. sqlserver 生成UUID随机码

    )) ) AS BEGIN ); ,),),),),) RETURN @id END --使用如下 select dbo.[FunGetUUID32](NEWID());

  7. Console中加入招聘等个性化信息

    try { if (window.console && window.console.log) { console.log("%c XX息科技 ", "f ...

  8. 无法import的原因(ImportError: No module named *****)

    python中,每个py文件被称之为模块,每个具有__init__.py文件的目录被称为包.只要模块或者包所在的目录在sys.path中,就可以使用import 模块或import 包来使用. 如果想 ...

  9. composite

    #coding:utf-8 import math # 在确定函数前,我们先来了解下算法 # 有数 n 判断其是否是合数 # 如果 n 除以 range(2,math.sqrt(n)+1) 能够整除, ...

  10. 解决mac os x下 tomcat启动报 java.net.BindException: Permission denied <null>:80 错误

    我在mac os x上启动tomcat的时候,报 java.net.BindException: Permission denied <null>:80,java.net.BindExce ...