mocha、chai、sinon和istanbul实现100%单元测试覆盖率
敏捷软件开发中,最重要实践的就是测试驱动开发,在单元测试层面,我们试着实现一个重要的指标就是测试覆盖率。测试覆盖率衡量我们的代码是否已经全部被测试到了。
但是指标本身不是目的,借助测试覆盖率检查,我们希望发现那些未被测试覆盖的代码,从而去思考如何测试那些代码的逻辑,进而更好的设计重构代码,让代码有更高的质量[1]。

谈到测试,正好最近在看《数学之美》,书中谈到的关于信息的一段话。我们要把代码的行为从不确定性,变成确定性,也是一样。从黑盒变成白盒,没有什么神奇的力量,唯有提供足够的信息,而测试中的断言就是信息,测试覆盖率也是信息,测试覆盖率可以认为是一种间接的信息,可以消除一部分不确定性,而充分的断言,则提供了更直接的信息。加上测试覆盖率检查,就能够提供足够的信息,来断言代码的行为是否符合期望。
测试的相关技术
Istanbul
是JavaScript
程序的代码覆盖率工具,以土耳其最大城市伊斯坦布尔命名。Istanbul会对代码进行转换,生成语法树,然后在相应位置注入统计代码,执行之后根据注入的全局变量的值,统计代码执行的次数;在对代码的转换完成之后,Istanbul会调用test runner
,例如mocha
,执行转换之后的代码的测试,生成测试报告。
Mocha
是一种测试框架,也就是运行测试的工具,类似Jasmine、Karma和Ava。跟JUnit的注解一样,mocha作为执行器,用descibe
和it
方法,来定义test suit,为不同的测试分组。mocha本身并不提供assert
断言,所以要提供更加有表现力的断言,可以搭配chai
使用,当然也可以使用nodejs提供的assert模块
。
在我们的代码中,总会有一些复杂的逻辑或者依赖io、网络的异步代码,用直接的方法难以测试,这时可以通过sinon
简化复杂代码的测试。Sinon通过创建Test Double也就是测试替身
,将我们代码中依赖的一些函数或者类,替换成测试替身,而我们可以对测试替身的行为进行设置,模拟我们的代码需要的结果,从而让难以测试的代码逻辑被执行。
为nodejs项目配置测试环境
1 安装相应的依赖包
mocha和istanbul可以全局安装,也可以只在当前项目安装。
npm install --save-dev mocha chai sinon istanbul
安装完成之后,在package.json
文件的scripts下,添加执行测试和测试覆盖率检查的命令
{
...
"scripts":{
"coverage": "istanbul cover _mocha -- -R spec --timeout 5000 --recursive",
"coverage:check": "istanbul check-coverage",
}
...
}
运行npm run coverage
和npm run coverage:check
,就可以生成测试报告,前者生成测试报告,后者则是检查测试覆盖率是否达到要求。

2 配置Istanbul
istanbul相关的执行参数,可以在命令行下执行时传递参数来制定,也可以在yaml格式的.istanbul.yml
文件中配置。简单贴出一些重要的配置项
instrumentation:
root: . # 执行的根目录
extensions:
- .js # 检查覆盖率的文件扩张名
excludes: ['**/benchmark/**']
... ...
reporting:
print: summary
reports: [lcov, text, html, text-summary] # 生成报告的格式
dir: ./coverage # 生成报告保存的目录
watermarks: # 在不同覆盖率下会显示使用不同颜色
statements: [80, 95]
... ...
check:
global:
statements: 100
branches: 100
lines: 100
functions: 100
最后的check是项目要通过覆盖率检查需要达到的测试覆盖率,测试覆盖率包括四个维度,分别是语句覆盖率、分支覆盖率、行覆盖率和函数覆盖率。如果达不到设定的指标,在执行的时候会报错,项目的测试就无法通过自动化的持续集成。
编写测试代码
敏捷软件开发中的测试驱动开发,意在通过先写测试,根据调用者的契约,设计如何实现代码,从而写出更加容易测试的代码,提高代码的质量。也是我们练习测试的应该考虑的方向。
1 一段简单的mocha测试代码
利用chai提供的expect断言,我们可以用BDD的方式,写出更加符合代码预期行为的测试用例。
var chai = require('chai')
chai.should()
var expect = chai.expect
var assert = chai.assert
describe('basic test', function () {
describe('simple', function () {
it('data check', function () {
var data = { name: "test" }
assert.isNotNull(data, 'data should not be null')
expect(data).to.be.an('object')
expect(data).to.have.all.keys(['name'])
expect(data).to.deep.include({name: 'test'});
});
});
});
2 用Sinon模拟文件读写
... 同上 ...
var sinon = require('sinon')
var fs = require('fs')
describe('sinon', function () {
it("should mock readFile", function(done){
sinon.stub(fs, 'readFile').callsFake(function (path, callback) { callback(new Error('read error')) })
fs.readFile("any file path", function(err,data){
assert.isNotNull(err)
done()
})
assert(fs.readFile.calledOnce)
});
});
在mocha测试框架中,如果我们调用的是异步的代码,那么需要显示的调用it回调函数的done方法,告诉mocha异步调用什么时候结束。否则的话,测试会挂起,直到设置的超时时间结束。
Sinon将测试替身分为spy、stub和mock,其中:
- Spy, 可以提供函数调用的信息,但不会改变函数的行为
- Stub, 提供函数的调用信息,并且可以像示例代码中一样,让被stubbed的函数返回任何我们需要的行为。
- Mock, 通过组合spies和stubs,使替换一个完整对象更容易。
本文的讨论篇幅有限,暂时不详细介绍各种sinon的使用方法,以后再通过其他文章专门介绍。
持续集成
完成所有代码之后,我们可以将代码发布到github,然后使用持续集成工具travis检查代码,将生成的测试报告上传到coverall上,这样就可以在项目中显示项目状态和测试覆盖率的badges。
具体使用方法,可以参看官方网站,使用coveralls需要在项目中安装依赖包npm i -D coveralls
。并且添加package.json执行脚本istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
通常的nodejs项目.travis.yml
配置如下:
language: node_js
node_js:
- "7.6.0"
install:
- npm install
script:
- npm test
after_script:
- npm run coverall
原文地址:http://www.51test.space/archives/2543
mocha、chai、sinon和istanbul实现100%单元测试覆盖率的更多相关文章
- 带你入门带你飞Ⅰ 使用Mocha + Chai + Sinon单元测试Node.js
目录 1. 简介 2. 前提条件 3. Mocha入门 4. Mocha实战 被测代码 Example 1 Example 2 Example 3 5. Troubleshooting 6. 参考文档 ...
- 使用 TypeScript & mocha & chai 写测试代码实战(17 个视频)
使用 TypeScript & mocha & chai 写测试代码实战(17 个视频) 使用 TypeScript & mocha & chai 写测试代码实战 #1 ...
- 大前端的自动化工厂(5)—— 基于Karma+Mocha+Chai的单元测试和接口测试
一. 前端自动化测试 大多数前端开发者对测试相关的知识是比较缺乏的,一来是开发节奏很快,来不及写,另一方面团队里也配备了"人肉测试机",完全没必要自己来.但随着项目体量的增大,许多 ...
- #单元测试#以karma+mocha+chai 为测试框架的Vue webpack项目(二)
学习对vue组件进行单元测试,先参照官网编写组件和测试脚本. 1.简单的组件 组件无依赖,无props 对于无需导入任何依赖,也没有props的,直接编写测试案例即可. /src/testSrc/si ...
- #单元测试#以karma+mocha+chai 为测试框架的Vue webpack项目(一)
目标: 为已有的vue项目搭建 karma+mocha+chai 测试框架 编写组件测试脚本 测试运行通过 抽出共通 一.初始化项目 新建项目文件夹并克隆要测试的已有项目 webAdmin-web 转 ...
- 学习Karma+Jasmine+istanbul+webpack自动化单元测试
学习Karma+Jasmine+istanbul+webpack自动化单元测试 1-1. 什么是karma? Karma 是一个基于Node.js的Javascript测试执行过程管理工具.该工具可 ...
- 带你入门带你飞Ⅱ 使用Mocha + Chai + SuperTest测试Restful API in node.js
目录 1. 简介 2. 准备开始 3. Restful API测试实战 Example 1 - GET Example 2 - Post Example 3 - Put Example 4 - Del ...
- nodejs 中使用 mocha + should + jscoverage 生成 单元测试覆盖率报告
最近一直在做nodejs,而关于js的单元测试覆盖率网上资料比较少而且吧比较零散,我从网上找来一些资料整理一下分析给大家,希望大家可以少走弯路. 首先我是从windows环境下测试的,用到的工具有 m ...
- [转载] JaCoCo:分析单元测试覆盖率的利器
转载自http://www.ibm.com/developerworks/cn/java/j-lo-jacoco/和http://www.cnblogs.com/chenfengmugu/p/4937 ...
随机推荐
- java对象流与序列化
Object流,直接把obj写入或读出. 前言: 比如 画图的程序,咣当画一个三角形出来,咣当画一正方形出来.然后存盘,当你下次再打开软件的时候三角形.方块还在原来的位置上.如果用面向对象的思维,三角 ...
- vue制作小程序--mpvue
mpvue是一个使用 Vue.js 开发小程序的前端框架 http://mpvue.com/ sass的使用 https://segmentfault.com/q/1010000014194954 n ...
- [转] TCPIP 网络协议层对应的RFC文档
TCPIP网络协议层对应的RFC文档 RFC - Request For Comments 请求注解 TCP/IP层 网络协议 RFC文档 Physical Layer Data Link Layer ...
- koa2开发入门
一.koa2入门 1.创建koa2工程 首先,我们创建一个目录hello-koa并作为工程目录用VS Code打开.然后,我们创建app.js,输入以下代码: // 导入koa,和koa 1.x不同, ...
- antlr提取代码注释
1. 来由 为什么要写提取注释呢,起因是工作需要.弄这么个不太重要的功能点来讲,旨在抛砖引玉. 一般而言,大家使用antlr解析源代码的时候,不会关心注释和空格之类内容,默认会过滤掉,不会放到语法树里 ...
- [WC 2018]州区划分
Description 题库链接 小 \(S\) 现在拥有 \(n\) 座城市,第 \(i\) 座城市的人口为 \(w_i\) ,城市与城市之间可能有双向道路相连. 现在小 \(S\) 要将这 \(n ...
- Node.js之Express四
Express提供的大部分功能是通过中间件函数完成的,这些中间件函数在Node.js收到请求的时点和发送响应的时点之间执行.Express的Connect模块提供了中间件框架,可以方便的在全局或路径级 ...
- 源码速读及点睛:HashMap
Java 8 HashMap的分离链表 从Java 2到Java 1.7,HashMap在分离链表上的改变并不多,他们的算法基本上是相同的.如果我们假设对象的Hash值服从平均分布,那么获取一个对象需 ...
- springboot 源码笔记
1.springAppication构造器 基于getSpringFactoriesInstances方法构造如下类(获取文件内容在META-INF/spring.factories文件中) 1.1 ...
- sql中替换字符串
select REPLACE(CONVERT(varchar ,CreateDate,23),'-','年') CreateDate from SG_Client 2018年06年11