JavaScript覆盖率统计实现

主要需求

1、 支持browser & nodejs

由于javascript既能够在浏览器环境执行,也能够在nodejs环境执行,因此须要能够统计两种环境下单元測试的覆盖率情况。

2、 透明、无缝

用户写单元測试用例的时候,不须要为了支持覆盖率统计多写代码,之前写的用例无需改动就能够直接统计覆盖率情况。

原理

javascript覆盖率的相关文章比較少。以下的图是通过阅读开源javascript覆盖率工具istanbul及开源測试框架Karma的覆盖率插件karma-coverage得出的。

javascript覆盖率统计的核心思想是,在源码对应的位置注入统计代码,当代码执行之后,依据统计代码统计的数据确定程序执行的路径,终于生成覆盖率统计报告。



1. 转换(instrument)

  • 使用开源工具Esprima对源码进行语法分析生成语法树
  • 在语法树对应的位置注入统计代码。在程序运行到这个位置的时候对对应的全局变量赋值,确保运行之后可以依据全局变量知道代码的运行流程
  • 使用开源工具Escodegen依据注入之后的语法树生成对应的javascript代码,即转换之后的代码(instrumented code)

注:这里进行语法分析的优点是,针对书写不规范的代码(比方一行多个语句),依旧可以非常好统计出分支覆盖和组合覆盖等信息。

2. 运行(run)

这一步须要先加载转换后的代码:

  • nodejs:直接通过对require语句进行hook来无缝实现,后面会具体介绍
  • 浏览器环境:须要将转换后的代码传给浏览器。假设是karma之类的带server的測试框架,须要通过socket传输至浏览量器,运行完之后再将包括覆盖率信息的运行结果传回server。生成測试报告

然后运行单元測试。产生的统计信息会挂在全局变量this以下。

对于浏览器环境,this就是window,而对于nodejs环境this就是global

3. 生成报告(report)

这一步会依据全局标量中的覆盖率信息生成特定格式的报告,如html、lcov、cobertura、teamcity等。

一个样例

//source code
function abs(num){if(abs > 0)return num;elsereturn -num;
}
//instrumented code
var __cov_iypKC$dWI6uJFmvxThycaA = (Function('return this'))();
if (!__cov_iypKC$dWI6uJFmvxThycaA.__coverage__) { __cov_iypKC$dWI6uJFmvxThycaA.__coverage__ = {}; }
__cov_iypKC$dWI6uJFmvxThycaA = __cov_iypKC$dWI6uJFmvxThycaA.__coverage__;
if (!(__cov_iypKC$dWI6uJFmvxThycaA['/Users/lonfee88/Codes/testframe/coverage-jasmine-istanbul-karma/abs.js'])) {__cov_iypKC$dWI6uJFmvxThycaA['/Users/lonfee88/Codes/testframe/coverage-jasmine-istanbul-karma/abs.js'] = {"path":"/Users/lonfee88/Codes/testframe/coverage-jasmine-istanbul-karma/abs.js","s":{"1":1,"2":0,"3":0,"4":0},"b":{"1":[0,0]},"f":{"1":0},"fnMap":{"1":{"name":"abs","line":1,"loc":{"start":{"line":1,"column":-15},"end":{"line":1,"column":17}}}},"statementMap":{"1":{"start":{"line":1,"column":-15},"end":{"line":6,"column":1}},"2":{"start":{"line":2,"column":1},"end":{"line":5,"column":14}},"3":{"start":{"line":3,"column":2},"end":{"line":3,"column":13}},"4":{"start":{"line":5,"column":2},"end":{"line":5,"column":14}}},"branchMap":{"1":{"line":2,"type":"if","locations":[{"start":{"line":2,"column":1},"end":{"line":2,"column":1}},{"start":{"line":2,"column":1},"end":{"line":2,"column":1}}]}}};
}
__cov_iypKC$dWI6uJFmvxThycaA = __cov_iypKC$dWI6uJFmvxThycaA['/Users/lonfee88/Codes/testframe/coverage-jasmine-istanbul-karma/abs.js'];
function abs(num){__cov_iypKC$dWI6uJFmvxThycaA.f['1']++;__cov_iypKC$dWI6uJFmvxThycaA.s['2']++;if(abs>0){__cov_iypKC$dWI6uJFmvxThycaA.b['1'][0]++;__cov_iypKC$dWI6uJFmvxThycaA.s['3']++;return num;}else{__cov_iypKC$dWI6uJFmvxThycaA.b['1'][1]++;__cov_iypKC$dWI6uJFmvxThycaA.s['4']++;return-num;}}

node.js集成覆盖率

通过hook能够直接无缝的载入转换后的代码。能够对以下两种语句进行hook:

  • require
  • vm.createScript

对require进行hook的代码是通过对Module._extensions['.js']进行赋值实现的:

function hookRequire(matcher, transformer, options) {options = options || {};var fn = transformFn(matcher, transformer, options.verbose),postLoadHook = options.postLoadHook &&typeof options.postLoadHook === 'function' ? options.postLoadHook : null;Module._extensions['.js'] = function (module, filename) {var ret = fn(fs.readFileSync(filename, 'utf8'), filename);if (ret.changed) {//加载instrument之后的代码并执行module._compile(ret.code, filename);} else {//加载原来的代码并执行originalLoader(module, filename);}if (postLoadHook) {postLoadHook(filename);}};
}

hook使覆盖率的集成变得简单。甚至不须要写代码,比方Mocha的覆盖率集成,仅仅须要改用例如以下的调用方式就可以:

istanbul cover _mocha -- -R spec test/spec

浏览器集成覆盖率

浏览器集成覆盖率就略微麻烦一点。好在istanbul提供了API:

  1. 转换代码(调用istanbul的Instrumenter接口)
  2. 将instrumented code发送到浏览器(自己实现)
  3. 将包括覆盖率信息的运行结果发回server(自己实现)
  4. 依据返回的覆盖率信息生成覆盖率报告(调用istanbul的Reporter接口)

转载于:https://www.cnblogs.com/jzdwajue/p/6900462.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/392665.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单元格内容分列多行_姓名太多,放在一列打印时浪费纸张,可以分成多行多列打印...

在日常工作中,往往会碰到这种情况(如下图):只有一列数据,而且比较多,如果打印起来就浪费纸张,然后复制、粘贴把表格变成几列,方便打印。今天小编和大家分享不用复制、粘贴,就能快速完成一列分成…

Java中接口、抽象类与内部类学习

2019独角兽企业重金招聘Python工程师标准>>> Java中接口、抽象类与内部类学习 接口与内部类为我们提供了一种将接口与实现分离的更加结构化的方法。 抽象类和抽象方法 抽象方法:仅有声明而没有方法体。 抽象类:包含一个或多个抽象方法的类&am…

docker导入镜像 liunx_docker扫盲?面试连这都不会就等着挂吧

推荐阅读:java喵:6大面试技能树:JAVA基础JVM算法数据库计算机网络操作系统​zhuanlan.zhihu.com一只Tom猫:都是“Redis惹的祸”,害我差点挂在美团三面,真是“虚惊一场”!​zhuanlan.zhihu.com现…

Linux iptables:规则原理和基础

什么是iptables? iptables是Linux下功能强大的应用层防火墙工具,但了解其规则原理和基础后,配置起来也非常简单。 什么是Netfilter? 说到iptables必然提到Netfilter,iptables是应用层的,其实质是一个定义规…

太阳系八大行星碰撞的视频_火星的身世:从太阳系的起源说起

大约46亿年前盘状的太阳星云从一大片又冷又暗的气体云中诞生太阳自己并没有任何暴露确切年龄的线索,我们之所以能够知道太阳系的“生日”,是因为迄今从陨石中找到的最古老固体物质,年龄约为45.68亿年。一般认为,太阳系的各个地方是…

博科查看光功率_法拉第旋光器:非互易性旋转光的偏振

法拉第旋光器是利用法拉第效应制作的光学器件,当入射光正向(或反向)进入旋光器时,入射光偏振面会发生旋转。法拉第效应1845年,法拉第发现:当一束平面偏振光通过置于磁场中的磁光介质时,平面偏振光的偏振面就会随着平行…

ps怎么对比原图快捷键_PS学习之旅:ps如何制作满天星,让你夜晚的天空图片更美...

ps学习之旅,本文介绍关于如何利用ps软件来制作满天星,让你夜晚的天空图片更美,操作很简单哦。1工具/原料Adobe Photoshop CS6软件图片一张2效果展示原图:效果图:3方法/步骤(1)打开PS,选择你想要加星星的一张…

永磁直驱风力发电机结构图_国内首台10MW海上永磁直驱风力发电机研制成功

2019首届新能源产业投融资论坛2019年10月25日周老师:157129595968月21日,具有完全自主知识产权、国内首台10MW海上永磁直驱风力发电机在东方电气集团东方电机有限公司研制成功,以此优异成绩向新中国成立70周年献礼。10MW海上永磁直驱风力发电…

struts2官方 中文教程 系列六:表单验证

先贴个本帖的地址,以免被爬:struts2教程 官方系列六:表单验证 即 http://www.cnblogs.com/linghaoxinpian/p/6906720.html 下载本章节代码 介绍 在本教程中,我们将探索使用Struts2来验证用户在表单上的输入。有两种方法可以来进…

c++ dll 类使用_在.Net Core 中使用钩子

目录前言什么是钩子使用钩子3. 挂载多个钩子4. 在钩子中加载额外的程序集5. 在 Asp.Net Web Api 项目中使用钩子结束语演示代码下载前言Host startup hook,是2.2中提供的一项新的功能,通过使用主机启动钩子,允许开发人员在不修改代码的情况下…

萨默尔机器人_助力产业发展 西安市人工智能机器人学会正式成立

8月23日,西安市人工智能机器人学会在西咸新区沣东新城协同创新港正式成立。西安报业全媒体记者 冯炜 摄8月23日,西安市人工智能机器人学会正式成立。学会将通过市场化机制、社会化服务等方式,整合科技创新资源和人才培养资源,促进…

h5滚动隐藏滚动条_这 10 个值得开启的隐藏功能,让你的 Chrome 释放更多潜力

上次分享了让 Chrome 浏览器用得更顺手的地址栏命令,跟大家整理和介绍了多个 Chrome 地址栏命令,利用好这些命令工具能够提升浏览器配置效率,让你的 Chrome 浏览器用得更顺手。这次介绍的是 Chrome 内置的实验功能,它被单独放在了…

Spring Boot干货系列:(二)配置文件解析

前言 上一篇介绍了Spring Boot的入门,知道了Spring Boot使用“习惯优于配置”(项目中存在大量的配置,此外还内置了一个习惯性的配置,让你无需手动进行配置)的理念让你的项目快速运行起来。所以,我们要想把S…

hibernate Criteria(条件查询接口)

Criteria&#xff08;条件查询接口&#xff09; // 1.简单查询 List<Customer> list session.createCriteria(Customer.class).list();// 2.条件查询: Criteria criteria session.createCriteria(Customer.class); criteria.add(Restrictions.eq("name",&quo…

记一次ArrayList产生的线上OOM问题

前言&#xff1a;本以为(OutOfMemoryError)OOM问题会离我们很远&#xff0c;但在一次生产上线灰度的过程中就出现了Java.Lang.OutOfMemoryError:Java heap space异常&#xff0c;通过对线上日志的查看&#xff0c;最终定位到ArrayList#addAll方法中&#xff0c;出现这个问题的原…

Google-Guava-EventBus源码解读

Guava是Google开源的一个Java基础类库&#xff0c;它在Google内部被广泛使用。Guava提供了很多功能模块比如&#xff1a;集合、并发库、缓存等&#xff0c;EventBus是其中的一个module&#xff0c;本篇结合EventBus源码来谈谈它的设计与实现。 概要 首先&#xff0c;我们先来预…

python之numpy

numpy是一个多维的数组对象&#xff0c;类似python的列表&#xff0c;但是数组对象的每个元素之间由空格隔开。 一、数组的创建 1.通过numpy的array(参数)&#xff0c;参数可以是列表、元组、数组、生成器等 由arr2和arr3看出&#xff0c;对于多维数组来说&#xff0c;如果最里…

git 上传

转载于:https://www.cnblogs.com/benbentu/p/6543154.html

Liferay 部署war包时候的deployDirectory 细节分析

引入&#xff1a; 在上文中&#xff0c;我们从宏观上讲解了Liferay部署war包的动作是如何触发监听器并且完成部署过程的&#xff0c;但是其中最核心的一块deployDirectory我们没讲&#xff0c;它的作用是当有了临时目录并且已经把war包的内容展开到该目录之后&#xff0c;是如何…

使用brew安装软件

brew 又叫Homebrew&#xff0c;是Mac OSX上的软件包管理工具&#xff0c;能在Mac中方便的安装软件或者卸载软件&#xff0c; 只需要一个命令&#xff0c; 非常方便 brew类似ubuntu系统下的apt-get的功能 阅读目录 安装brew 使用brew安装软件 使用brew卸载软件 使用brew查询软…