[Node.js] Express的测试覆盖率
原文地址: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 的覆盖率测试:
- 全局安装 mocha ,istanbul 及 express
npm install -g mocha
npm install -g istanbul
npm install -g express
- 生成一个express 站点:
express -e express-coverage
- 修改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" : "*"
}
} - 把自带的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; - 在项目下新建一个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();
});
});
}); - 输入命令
npm run-script test-cov得到覆盖率报表:
- 指标有点低是不是,因为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: {}
});
}); - 在 router.js 原有场景中,添加一个用例,加上对404代码行的触发:
it('404 response', function (done) {
request(app)
.get('/non/')
.expect(404)
.end(function(err, res){
if (err) throw err;
done();
});
}); - 再次输入命令
npm run-script test-cov查看覆盖率报表,我们能看到进步 :)
后记
找到合适的 Mock工具和测试框架并进行整合,Web测试及覆盖率报表获取的思路大抵如此。关于测试框架的各种参数组合和花样玩法,还有很多有意思的功能(比如和 Travis-CI、Coveralls.io 等公共服务集成,在仓库上展示项目状态徽章),本文不再赘述,有兴趣的可加node学习交流群一起探讨。
更多文章请移步我的blog新地址: http://www.moye.me/
[Node.js] Express的测试覆盖率的更多相关文章
- Windows下Node.js+Express+WebSocket 安装配置
Linux参考: Linux安装Node.js 使用Express搭建Web服务器 Node.js是一个Javascript运行环境(runtime).实际上它是对Google V8引擎进行了封装.V ...
- node.js + express 初体验【hello world】
[node.js] 一个神奇的XX 呵呵 :) 不知道怎么形容他才好! [express] 是node.js 开发web应用程序的框架 开发环境:XP 大家共同进步吧 :) 一:前期准备: 1:下载 ...
- Node.js+Express+MongoDB数据库实现网页注册登入功能
通过 Node.js + Express + MongoDB 实现网页注册账号 和 登入账号的功能 项目准备: 1: 事先准备好项目的页面 (首页页面 index.html)(登入页面 login.h ...
- node.js+express+mongoose实现用户增删查改案例
node.js+express+mongodb对用户进行增删查改 一.用到的相关技术 使用 Node.js 的 express 框架搭建web服务 使用 express 中间件 body-parse ...
- Node.js Express 框架学习
转载:http://JavaScript.ruanyifeng.com/nodejs/express.html#toc0 感觉很牛的样子,不过觉得对初学者没太大用,里面很多例子用的api都没有详细的说 ...
- Node.js Express 框架
Node.js Express 框架 Express 简介 Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP ...
- Nodejs学习笔记(六)--- Node.js + Express 构建网站预备知识
目录 前言 新建express项目并自定义路由规则 如何提取页面中的公共部分? 如何提交表单并接收参数? GET 方式 POST 方式 如何字符串加密? 如何使用session? 如何使用cookie ...
- React+Node.js+Express+mongoskin+MongoDB
首发:个人博客,更新&纠错&回复 采用React + Node.js + Express + mongoskin + MongoDB技术开发的一个示例,演示地址在这里,项目源码在这里. ...
- Node.js Express框架
Express 介绍 Express是一个最小的,灵活的Node.js Web应用程序框架,它提供了一套强大的功能来开发Web和移动应用程序. 它有助于基于Node Web应用程序的快速开发.下面是一 ...
随机推荐
- Dreamweaver8 查找和替换窗口不见了解决办法
激活窗口,按下Atrl+空格,再按下M,方向键移动窗口,就回来了!
- iOS javascript js 交互
//JS里的一个回调.比如网页上的某个按钮点一下之后.JS会调用setKey这个方法. JSContext *context1 = [self.startView valueForKeyPath:@& ...
- 深入学习golang(3)—类型方法
类型方法 1. 给类型定义方法 在Go语言中,我们可以给任何类型(包括内置类型,但不包括指针和接口)定义方法.例如,在实际编程中,我们经常使用[ ]byte的切片,我们可以定义一个新的类型: type ...
- Codeforces Round #182 (Div. 1)题解【ABCD】
Codeforces Round #182 (Div. 1)题解 A题:Yaroslav and Sequence1 题意: 给你\(2*n+1\)个元素,你每次可以进行无数种操作,每次操作必须选择其 ...
- 2013年ACM湖南省赛总结
今年的比赛最大的变化就是改用OJ判题了,相比于PC^2确实省事了不少,至少可以直接复制样例了.题目方面依旧是刘汝佳命题,这点还是相当好的,至少给人以足够的安全感. 开始比赛之后安叔瞬间就把前半部分题目 ...
- drupal7 Views Slideshow 简单教程
一.下载安装(略) 二.内容类型建立(过程略,首页幻灯),字段建立(过程略)主要有2个字段,图片字段 和 指向链接字段 三.view 1.建立一个新的view,名称为frontbanner 显示为内容 ...
- [AX2012 R3]在SSRS报表中使用QR二维码
AX2012是自带生成QR二维码的类,可以很方便的用在SSRS报表中,下面演示如何在RDP的报表中使用二维码,首先从定义临时表开始: 字段URL是要用于二维码的字符串,QrCode是container ...
- 领域驱动开发推荐代码示例 — Microsoft NLayerApp
简介: Microsoft NLayerApp是由微软西班牙团队出品的基于.NET 4.0的“面向领域N层分布式架构”代码示例,在codeplex上的地址是:http://microsoftnlaye ...
- TabHost的用法(转)
本文结合源代码和实例来说明TabHost的用法. 使用TabHost 可以在一个屏幕间进行不同版面的切换,例如android自带的拨号应用,截图: 查看tabhost的源代码,主要实例变量有: pr ...
- 顺序执行到来的消息 actor
在某项目里,有个 actor 需要做一些持久化的操作,这些操作耗时比较久,理应使用异步的代码来写,但是需求又强调每次只能做一个持久化操作,后来的请求应该等待.一个显然的做法是阻塞式的写,这样就能比较简 ...