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 等覆盖率数据文件:
实例
mock 工具有了, 测试框架和覆盖率工具也有了,就差实战了。下面举个粟子看看怎么做 Express 的覆盖率测试:
- 全局安装 mocha ,istanbul 及 express
1
npm
install
-g mocha
1npm
install
-g istanbul
1npm
install
-g express
- 生成一个express 站点:
1
express -e express-coverage
- 修改package.json如下,并使用
npm install 安装需要的包:
12345678910111213141516171819202122{
"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:
1234567891011
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,并编写用例:
12345678910111213141516171819202122232425262728
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的生成代码:
123456789101112
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代码行的触发:
123456789
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学习交流群一起探讨。
express, mocha, supertest,istanbul的更多相关文章
- nodejs+mocha+supertest+chai进行测试(only demo)
1.nodejs安装成功 (上一篇:brew install nodejs) 2.mocha安装成功 npm install -g mocha 解释: -g代表global,全局的意思.此处mocha ...
- mocha、chai、sinon和istanbul实现100%单元测试覆盖率
敏捷软件开发中,最重要实践的就是测试驱动开发,在单元测试层面,我们试着实现一个重要的指标就是测试覆盖率.测试覆盖率衡量我们的代码是否已经全部被测试到了. 但是指标本身不是目的,借助测试覆盖率检查,我们 ...
- [Node.js] Express的测试覆盖率
原文地址:http://www.moye.me/2014/12/03/express_coverage/ 引子 有群友问到Express怎么做 单元测试/覆盖率测试,这是上篇所遗漏的,特此补上 Exp ...
- node js 调试方法
1. node-debug tutorial 大家对nodejs调试应该都比较头疼,至少我这个不用IDE写js的人很头疼这个,其实node的生态圈非常好 有非常好的工具和非常潮的开发方式 这里总结了3 ...
- npm package.json属性详解
概述 本文档是自己看官方文档的理解+翻译,内容是package.json配置里边的属性含义.package.json必须是一个严格的json文件,而不仅仅是js里边的一个对象.其中很多属性可以通过np ...
- [译]为什么我要离开gulp和grunt转投npm脚本的怀抱
原文链接:https://medium.freecodecamp.com/why-i-left-gulp-and-grunt-for-npm-scripts-3d6853dd22b8#.n7m1855 ...
- (译)package.json详解
原文链接 概述 本文囊括了所有package.json文件中你需要知道的细节.注意package.json必须是纯JSON的,而不仅仅是一个JavaScript对象字面量.该文件描述的很多行为都受np ...
- npm-package.json
Specifics of npm's package.json handling DESCRIPTION§ This document is all you need to know about wh ...
- WePY - 小程序敏捷开发实践丨掘金开发者大会
声明:内容转载他处,如有侵权,可协商下架 本主题虽然在其它地方讲了很多次,但还是有非常多新内容.因为很多东西正在做或者想要做.本次分享主要分为以下几个部分: WePY 的介绍 WePY 的用户 上面展 ...
随机推荐
- C++父子类继承时的隐藏、覆盖、重载
存在父子类继承关系时,若有同名成员函数同时存在,会发生隐藏.覆盖和重载这几种情况.对于初学者也比较容易混淆,为此,我整理了一下我的个人看法,仅供参考.希望对大家理解有帮助,也欢迎指正. 1.父子类继承 ...
- sqlite-在数据库中创建默认时间
create table log( content ), logtime TIMESTAMP default (datetime('now', 'localtime')) )
- JSON的String字符串与Java的List列表对象的相互转换
1.JSON的String字符串与Java的List列表对象的相互转换 在前端: 1.如果json是List对象转换的,可以直接遍历json,读取数据. 2.如果是需要把前端的List对象转换为jso ...
- makefile之shell函数
shell函数不同于除"wildcard"函数之外的其它函数.make可以使用它来和外部通信. 函数功能:函数"shell"所实现的功能和shell中的引用(` ...
- Reveal使用教程
Reveal使用教程 Reveal是用于透视程序整体结构的一个软件,软件收费89美刀,试用期30天,不过好在有破解版,无需担心花钱的问题 在然后呢,软件在哪下,可以在我的github上下载到破解版本 ...
- ruby之各种概念
一.引言 刚开始接触ruby,遇到问题于是上网查资料,但是有时候却又看不懂,这很大一部分原因是我不知道一些关于ruby的概念名词是什么意思,所以看了别人的回答也理解不了. 二.各种名词 ruby:这个 ...
- PL/SQL开发五年工作经验精典实例
1. minus(差集)与intersect(交集) minus指令是运用在两个SQL语句上.它先找出第一个SQL语句所产生的结果,然后看这些结果有没有在第二个SQL语句的结果中,如果有的话,那这一笔 ...
- love2d教程33--window模块
--love的window模块比较简单,直接贴代码了function love.load() io.stdout:setvbuf("no") -- 设置io为无缓存模式 --获取显 ...
- git patch生成方法
先把改动commit掉,然后生产改动patch给提交代码的同事,详细操作过程例如以下: 改动代码的同事: git format-patch al821_xxx origin/al821_xxx 会生成 ...
- TCP/IP详解读书笔记:概述
分层 分层是一种很通用的架构模式.通过分层,可以把一个系统分解成多个层,每个层专注于各自的功能,并提供接口给上面的层调用.上面的层不需要了解调用层的详细实现,只依赖于其接口,这就给维护带来了很大的好处 ...