「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战」
前两天已经实现了Jest
测试框架的环境搭建、基本测试用例的编写以及怎么解读测试覆盖率,没接触的建议去看看,附文章链接:
当我们执行jest --coverage
,它会生成如下的测试报告:
它是这么实现的呢?
原理猜测
通过生成的指标看出jest
框架生成的覆盖率对语句、函数、分支、行数这4个维度进行了生成,如果对js编译原理有所了解,根据敏锐度大体能过猜测到应该是需要对js
做ast
树解析,因为这几个指标的类型,在对应的ast数的节点类型上都有对应的体现。当解析成ast
树,应该的存在某种机制能往树里面侵入部分统计代码,那么在执行的时候遍可以统计ast树哪些类型的节点是已经被执行了。
原理验证
了解库的实现原理,通常第一步是打开类库的package.json
文件,看看它都依赖了什么。通过对该文件的排查,我们排除到这三个库:
istanbul-lib-coverage
:提供覆盖率信息的只读视图,能够合并和汇总覆盖率信息的apiistanbul-lib-report
:istanbul库生成报告的核心程序istanbul-reports
:大体提供报告输出的公共能力api
这三个库使用围绕着istanbul
,因此该库基本大概率提供了核心的覆盖率能力,下面去验证下。
istanbul库
istanbul,为ES5和ES2015+JavaScript代码插入行计数器,这样您就可以跟踪单元测试对代码库的运行情况。
下面我们安装个看看:
npm install -g istanbul
复制代码
安装完成后,执行下istanbul help
查看下它所支持的命名,所有核心命令如下:
check-coverage
:根据JSON文件的覆盖率阈值检查总体/每个文件的覆盖率。如果不满足阈值,则退出1,否则退出0。cover
:透明地将覆盖率信息添加到节点命令。在执行结束时保存coverage.json和报告。instrument
:插入文件或目录树,并将插入指令的代码写入所需的输出位置。report
:为上一次运行中生成的JSON对象写入覆盖率报告。
instrument命令
改指令会插入代码,通常查找资料,它有个名称为叫:函数插桩
,会给函数添加些代码。下面验证下:
// test.js文件
const toSum = (a, b) => {
if (a > 10) {
return a + b + 10;
}
return a;
};
toSum();
复制代码
对test.js
文件执行该命令添加代码,并输出到test-inst.js
文件:
istanbul instrument ./test.js -o ./test-inst.js
复制代码
转换后的代码,通过格式化后如下:
可以看出如下信息:
- 它创建了一个全局对象用于存储统计信息
// 形式如下:
var coverages = {
'/root/test.js':{
path: '/root/test.js',
// statement数量
s: {},
// branch数量
b: {},
// function数量
f: {},
// 记录function的开始结束位置
fnMap: {},
// 记录statement的开始结束位置
statementMap: {},
// 记录statement的开始结束位置
branchMap: {}
}
}
复制代码
- 它往函数执行代码各个对应位置插入了统计代码,对应代码块执行就
+1
- 命名及其复杂防止与全局命名冲突。
这便是整个istanbul
的流程,也是生成覆盖率的基本原理。
总结
这篇大体说明了jest
测试覆盖率的生成原理,它的基本实现机制我们是能够了解的。至于istanbul
库是如何实现函数函数插桩
机制,它的原理,有空再说。