Java的测试覆盖率工具

对于Java语言的测试覆盖率工具众多,有开源免费的Jacoco/PIT,也有商业的Clover,也有开发了十多年目前还在发布的的覆盖率工具Cobertura,

  1. Emma (http://eclemma.org/jacoco/)
  2. JCov (https://wiki.openjdk.java.net/display/CodeTools/jcov)
  3. Code Cover (http://codecover.org/)
  4. Cobertura (http://cobertura.github.io/cobertura)
  5. PIT (http://pitest.org/)
  6. Clover (https://www.atlassian.com/software/clover)
  7. Jacoco (http://eclemma.org/jacoco/)

下图将上述覆盖率工具的功能、授权方式、代码的注入、报告和语言支持、对各个工具的集成支持、开发活跃度一一列出,方便大家进行一些比较,(其中Emma已停止开发,其被Jacoco取代,不再列入)

java_cov_tools_comparison

表格中代码注入方式的含义如下,

-- source code: 在源代码编译时刻进行注入

-- bytecode offline: 对编译后的类二进制文件进行注入,在JVM载入内存前

-- bytecode on the fly: 在JVM载入二进制文件时使用application classloader动态注入

比较推荐使用Jacoco,各种工具的支持比其它开源工具更好,社区也很活跃,个人也在使用其开发覆盖率平台的工具,使用下来感觉还不错。

Jacoco提供了丰富的API接口,其开发文档和相关小工具还是比较多,目前主要集成在各个开发工具和构建分析工具中,开发人员一般在IDE中使用各种Jacoco插件分析代码,测试人员基本上通过ant/maven工具生成覆盖率报告,然后将报告展现在CI/Sonar等各个工具上。但是Jacoco的功能是不仅仅于此,比较遗憾的是还没有一个通用的覆盖率收集平台工具,能够为测试活动提供一个整体解决方案,对于测试,一般希望能够按阶段按环境来收集覆盖率数据,能够实时监控不仅仅来自自动化测试的数据,还可以为手动测试提供实时监测数据和报告,能够分析增量代码的覆盖率数据。目前的覆盖率工具基本以插件的方式存在,功能和报告分散,也许在不久的将来会有个覆盖率平台能够开发出来解决这个问题,当然这个覆盖率平台也一定不仅仅支持Java语言。

 

参考资料,

-- Clover官方wiki对各个覆盖率工具的比较: Link

-- Wiki上对各个覆盖率工具的比较:Link

-- SonarQube对覆盖率工具的介绍: Link

-- IBM DeveloperWorks对Jacoco的介绍:Link

 

代码分支管理模式和应用场景

在一个有着上千开发技术人员的大型互联网公司,当研发到一定阶段,企业对CI/CD(持续集成/持续交付)的考虑会越来越重视,项目的快速上线交付能力考验着开发测试运维各个团队,但是实现CI/CD不是件容易的事情,肯定会对项目的开发上线有个规范过程,需要有个统一的应用框架,需要有规范化的部署,需要稳定可靠的基础设施,最近有对代码分支管理及其应用场景的讨论,看选择哪个可以方便CI。

两种代码分支开发模式

现在的代码分支管理一般按照上线流程成两个流派,一个是分支发布模式,还有一个是主支发布模式,下面是对两个分支模式的定义,

分支发布模式每次上线都会开出一个分支,上线的发布包从这个分支构建。

主支发布模式主支发布模式中,每次上线的发布包都会从代码主支构建。

需要说明的是,网上大家讨论比较多的gitflow流程是一种主支发布模式,其发布tag会在构建前预先提交到代码仓库,并且标记包的版本,这个和本文CI最佳实践中是有些出入,如何标记代码的tag,及其如何在CI平台中构建出包并确定包的版本,这个将在下面讨论。

分支发布流程

见如下图,

master_release4

版本1.0要上线时,会先开出一个分支R1.0,然后使用这个分支构建打包上线,上线之后分支的提交权限关闭。当发现线上有紧急bug修复时,则直接提交hotfix到分支R1.0,然后继续从分支R1.0构建打包上线。

分支R1.0和R1.1对代码提交有权限控制。

主支发布流程

见如下图,

branch_release4

版本1.0要上线时,需要合并代码到master,然后从master构建打包上线,上线之后master的提交权限关闭。当发现线上有紧急bug需要修复时,从版本1.0的commit id拉出一个hotfix分支,修复代码提交到hotfix分支,然后从此分支构建打包上线。

master和hotfix对代码提交有权限控制。

两种代码分支管理的简单比较

1. 主支发布模式中,发布主支一般固定不变,这有助于CI的打包构建,不用每次上线都要在CI job中切换分支,这是比分支发布更好的地方,在这个模式下,CI流程中可以只以主支的代码打出来包上线,然后代码合并权限和审查流程也可以较好的进行工程化控制。

2. 分支发布模式中,每次大的版本上线都有一个对应的分支,这样做有个缺点是,分支会越来越多,分支名称如果管理的好,还可以分辨出各个分支开发的功能作用,但如果管理的不好,分支可以多到让自己看不清楚哪个分支是做什么。但是,这对紧急修复是非常有帮助的,一旦知道哪个版本有问题,可以快速切换到相应分支,进行修复。

CI的版本控制

包的版本可以在很多地方确定,有些使用代码仓库管理发布包版本,有些在CI平台上控制出包的版本,有些则在上线发布平台上控制。

我觉得在CI平台上来控制包的上线版本是最恰当的时候,因为每次构建的时候,都应该是不同的包,有不同的版本,都需要经过测试才能上线。我们不能因为代码一样,构建两次的包其版本都一样,这个在后续部署测试上线环节都容易混淆。

代码的tag功能和版本控制

有些项目团队使用tag功能来确定上线包的版本,一旦有新的版本tag标记,则触发CI平台上构建打包job,获取tag来每次进行构建,但是tag有一个缺点,其版本变化都需要预先手动更改,如果CI平台对同一个tag的代码进行多次构建,这是很有可能的,这个时候如何确定版本是个问题。

一般来说由CI平台来控制包版本是推荐的做法,而不是通过代码仓库中的tag或者配置文件来控制。在软件行业,几乎没有人没有听说过maven这个项目构建工具,其默认使用项目中的pom.xml进行构建,获取依赖包,并打出一个完整的项目包,包的版本会以pom.xml中版本来定,但是有多少了解 mvn中有个version:set插件,用于在CI构建的时候动态更新pom.xml的包版本?在pom.xml等这些构建配置文档里确定包版本,提交到代码仓库,通过这个方式来确定构建时候的包版本是不可取的。maven打出的包,如果是上传到snapshot仓库,是可以覆盖上传的,但如果上传到release仓库,则必须修改每次打包的版本,这个时候一般使用mvn version:set这个插件,在CI构建的时候动态确定包的版本,而不是把包版本提交代码仓库。

然后tag该如何添加,有什么用呢?我们知道tag一般用于表示代码快照,一般的commit id是不可读的,tag我们可以做些有意义的命名,来告诉我们这份代码快照是做什么的,很多时候tag就是包的上线版本。那么上线版本什么时候打到代码仓库中去?在我们的实践中,我们是在包正式上线后,才把上线的版本打到相应的commit id上,然后如果有紧急问题需要修复上线,能够通过tag快速拉取代码,打hotfix。

应用场合

两个分支开发模式是互有优缺点,至于选择哪个更好,其实和业务场景紧密相关的,不同的业务场景需要不同代码分支管理方式,项目开发起来才会方便开发。

我们将软件应用开发下面两大类,

1) 桌面应用

一些桌面安装应用,其特点是,应用的安装包分发和版本升级都由用户在其桌面系统中控制,应用的版本基本由用户的使用市场占有率决定,开发企业很难控制应用的版本升级,完全靠用户的评价和喜好,其版本控制是趋于分散开放模式。

这些应用包括普通的windows、mac应用,还包括操作系统本身,像微软xp和win7,IE的浏览器等等,这些应用的版本,企业是很难控制的。以微软开发的XP系统为例,在微软2014年4月全面停止支持服务后,到2014年11月份StatCounter的统计数据显示Windows XP的市场份额仍然高达10.71%,和Windows 8.1的10.86%占有率几乎持平。微软公司很早就说要关闭Windows XP,但是XP的市场占有率让微软公司无可奈何,谁让XP的用户那么坚定执着。

从中可以看到,桌面应用的版本控制是分散开放的,市场上有多少版本的应用,作为软件企业需要考虑市场占有率,然后决定维护多个版本的紧急修复和补丁升级,老版本的维护工作一点都不轻松。

2) web和移动应用

现在是互联网和移动应用的时代,这个时代有着鲜明的特征,快速开发发布和迭代,其应用的版本控制和升级也是和桌面应用完全不一样。web项目的前后端版本和升级,完全是软件公司控制的,如果后台支持灰度发布,那么在灰度发布期间,一般有两个版本的应用在运行,很少有三个版本以上的同一应用同时运行。移动应用的版本升级比web应用比较慢,但还是比桌面产品好,无论是android还是ios,都有相应应用市场,不断的推动用户升级至最新版本,现在移动应用本身也在加强自动升级功能。

可以看到,web和移动应用的版本是不断向前升级的,是一种趋于内敛的版本控制。

那么,现在回到刚刚问到的问题,在什么场合,我们该用什么样的分支管理模式呢?简单的回答如下,

1)软件产品的版本升级完全由用户来决定,版本控制趋于分散开放,这个时候建议使用分支发布开发模式,大部分桌面应用就是属于这个类型。

2)软件产品的版本完全受企业控制,趋于内敛聚合,可以不断升级到最新的版本,这个时候使用主支发布的开发模式,会使项目管理和开发更加方便。web和移动应用推荐使用这个模式。

具体原因,其实就是,如果版本不能控制,各个版本的代码最好能有单独的分支管理,其紧急hotfix开发和补丁程序能够较好的独立开发,代码的hotfix合并也比较清晰易懂。如果版本完全受控制,那么我们只要负责最近发布的几个版本,而且很多时候,我们只要负责最新一个版本,这个时候即使有紧急hotfix,我们也可以把紧急fix打到主支上,然后上线,完全没有维护N多个分支(N>无穷大)的烦恼。

 

 

探索性测试和机器学习

最近在研究如何做项目的测试,在我们平常的项目中,有功能性测试,回归测试,集成测试,为了提供测试效率和控制质量,会尽可能的做自动化测试,但最近的研究和这些测试有点不一样,我们准备尝试下做一种探索性测试,最好能够让机器一边学习一边帮我们测试,我们所需要的就是做一些假设性输入即可。

智能探索性测试

探索性测试(explortary test),一般没有计划好的测试步骤,没有预设的测试目标,只有对测试结果的普遍期望,这个和随机性测试(ad-hoc)相同,不一样的地方是,探索性测试是在对测试的产品并不十分了解,是根据测试者的经验和一些测试常识来做一些测试,一边学习,一边不断的测试新功能。

人的学习能力是很强的,所以很多时候探索性测试被认为是只有人可以胜任这个工作,但是大家可能忽略的一点是,测试需要很强的重复执行能力,在这个方面,人是远远无法和机器相比的。假如我们能够让机器拥有人类的学习能力,那么是不是可以让机器帮我们做探索性测试。

举个例子,在web测试中,如果我们能够让机器能够识别一个链接,点击后期望是打开一个页面,是不是可以让机器帮我们测试网页上的所有链接?在移动应用中,如果我们能够让机器识别一个按钮,点击之后期望应用没有运行异常,可能还会弹出一个对话框,如果点击对话框的“关闭”按钮,那么对话框应该可以关闭;甚至可以这样,机器可以点击“确定”按钮,机器记录下所发生的一些事件,那么当一次点击这个按钮时,机器是否就可以判断点击同样按钮的事件?

我们想这是完全可以做到的,现在的机器学习理论已经可以让我们好好对探索性测试做些工作,我们可以给机器输入一些预设条件,然后机器就可以自己学习摸索,然后自动帮我们做测试工作,出现异常能够报告出来,任何测试如果能够被自动化,就应该自动化,探索性测试也不例外,我们现在欠缺的可能就是一个智能的机器了。

机器学习算法

要让机器能够变得智能化,我们需要让机器能够自我学习和判断,这样才能自主开展探索性测试工作。让我们先从几个简单的机器学习算法说起。

邻近算法

这是一个简单的算法,其原理是,选取一个事物的特征项,每个特征项都可以量化,有量化的值,比如一个长方形的有两个特征项:长和宽,我们以(x,y)来表示,那么任何一个长方形都有这两个值,邻近算法就是比较两个长方形的特征项值的坐标距离,如果离的近,说明这两个长方形就是同一事物。

概率算法

这个算法和邻近算法不一样的是,其根据每个特征项的概率综合起来判断事物的归类,这里面有不同的概率组合算法,我们不在文章里展开。

举个例子来说,小米相机上自带有识别人脸,其会判断年龄和性别,在这个过程中,就有一个机器判断过程,那么机器如何判断年龄和性别么?一个人的脸上有很多特征项,有头发,眼睛,鼻梁,眉毛,嘴巴,皮肤,以判断性别来说,头发是一个很重要的特征项,头发长有90%的可能是女性,皮肤白有60%可能是女性,那么两者加起来判断可能有96%的可能为女性,这个是条件概率得到的判断,如果有更多的特征概率,则判断会更准确。

一个简单的实现

在我们的项目中,我们以一个web网页为基础,希望获取到网页上所有图片类型并加以归类,开发语言用的是Python。

代码地址:https://git.oschina.net/pphh/machinelearning.git

算法步骤,

1. 读取训练样本数据,建立数据模型,获取k值,k值是判断目标数据类型的个数。

2. 输入目标数据,计算和样本数据特征值的坐标距离。

3. 获取k个最近距离的样本数据,并根据样本数据的类型来决定目标数据的类型。假如k为3,如果有两个以上的样本数据表明同一类型A,则目标数据就可以判断类型A。