前言:本文主要实现的功能是代码增量覆盖率,且基于覆盖率已实现,可拿到全量覆盖率结果的情况下,给出的增量代码覆盖率方法实现,虽然各类语言有差别,但是实现的基本思路都是一致的
名词解释:
增量代码:增量代码指的是相对于master一个分支新增的代码,通常对应我们的测试分支,可以通过分支的diff得出
覆盖率:指的是请求过程中,覆盖哪些行代码,本文主要针对行代码覆盖率进行阐述,不适用与方法覆盖率
语言:目前主流的语言,JAVA可以通过JACOCO实现覆盖率,GO通过SDK即可实现,我在实现的过程中是基于PHP语言做的,需要使用XDEBUG插件
1,基本原理
计算覆盖率第一步需要拿到diff,即你的分支diff,在拿到分支diff后,我们要通过覆盖率的结果文件与diff中的代码进行对比过滤,如果覆盖率的结果在本次diff中,说明本次测试覆盖了该行
流程图:
2,实现难点
a:如何计算历史覆盖率
b:如果是主干上线,在master不断迭代的情况下会产生很多已上线,但不属于本次测试范围的代码,此时应该以什么为基准与分支进行diff
c:如果同一分支进行两次提交,第一次被覆盖的行收到了第二次提交的影响,但是处理历史覆盖率时,这部分代码应该如何进行统计
3,diff计算方法
在做diff时,首先要了解一些git的分区概念以及常用的命令,这里引用这篇文章https://www.cnblogs.com/kisun168/p/11408346.html,如果你们有代码服务的平台(封装的gitlab接口,通过接口实现一些常见的git操作),一般可以通过接口调用解决,但是如果没有代码服务平台,此时就需要借助.git目录来完成增量代码的计算,由于大家不一定都有代码服务,本文会以基本的git命令来完成这些接口实现‘
注意:在完成以下操作时,应该更新本地仓库,使本地仓库与远程仓库保持一致,否则会造成diff出现误差,关于git的操作,我们不会考虑git commit的撤销情况,即认为commit是不断增加的,commit不会被删除
a:
获取一个分支最新的commitid eg:bb4f92a7d4cbafb67d259edea5a1fa2dd6b4cc7a
git rev-parse HEAD
获取short commit id eg:bb4f92a
git rev-parse --short HEAD
b:
如果master有提交,而此时你本地的分支又没有合并master,此时应该怎样diff呢
此处需要了解一个git命令
git merge-base [-a|--all] <commit> <commit>…
原理图
c:我们通过git diff commit1 commit2来获取diff内容 commit1是 git merge-base计算出来的公共commitid commit2是这个分支最新的commit,此时我们即可获得两个分支的diff,同时忽略掉来master更新的影响
结论1:此时我们获得了分支diff的方法
d:在拿到计算方法以后,我们如何计算在此次diff中都修改了哪些内容呢,可以参考下图
diff --git a/serivce/executebyapi.py b/serivce/executebyapi.py
index 20dc848..c02b633 100644
--- a/serivce/executebyapi.py
+++ b/serivce/executebyapi.py
@@ -201,6 +201,7 @@ def executeII(commitid,branch,moduleid):
#将覆盖率数据赋值到tempdiffcontent,然后进行插入操作
for item in coverageresult.keys():
coveragedline = coverageresult[item]
+ coveragedline = [str(x) for x in coveragedline]
for i in range(len(changelist)):
tempdata = changelist[i]
diffclass = tempdata['filename']
@@ -214,7 +215,7 @@ def executeII(commitid,branch,moduleid):
filename = getattr(temdata,'filename')
coverageline = getattr(temdata,'coveragedline')
-
+
for i in range(len(changelist)):
data = changelist[i]
图中是常见的一个difflog日志,如何能从这个log中拿到我们想要获取的信息呢,如果做行覆盖率,那么我们此时肯定关心这个diff都影响到了哪些行,哪些行代表新增的
概念明确:此处我们来细化一些difflog的含义
规则1:图中的-代表仅出现在commit1中的内容,图中的+代表仅出现在commit2中的内容
第一行:是第一次提交与第二次提交对应的两个文件
第三行:commit1的文件
第四行:commit2对应commit1的文件
第五行:@@ -201,6 +201,7 @@ 这串数字代表的含义是,从a文件开始的201行开始计算(包括201行)向下数6行的内容有改动,这段改动对应到新文件是 b文件的201行,(包括201行)向下数7行有改动(这里需要我们注意的两个点是,1:201行指的是从 “#将覆盖率数据赋值到“ 那一行开始计算的,并不包括def executeII这一行,该行提示的通常是哪个方法或者哪个类,2,为什么对应到新文件是7行,因为diff中有一行+号,在计算新提交对应改动的范围是,必须算上这一行,但是老提交不必计算,这就是根据规则1得来的+标记的内容是仅出现在新提交中的内容) 通常,返回的这个数据就代表了两次提交都改动了哪些行
解析log:此时我们会认为,这次最新的提交b文件的204,218行是这次新增的内容,如果这两行也在覆盖率中,那么就代表被测试覆盖了
结论二:此时我们获得了计算两次提交改动了哪些行的方法
通过上述方法,我们可以总结归纳出一些结论,这些结论在我们进行计算历史覆盖率时是有帮助的
a:在git中一行代码存在的三种情况
被删除:只存在于原文件中
被改动:将原行删除,对修改后的代码重新标记,只存在于新文件中
行号变动:可以理解为,在这一行的上边新增或删除了代码,导致了这行代码的行号是变动的,实际这一行的代码内容我们通过diff是没有办法确定是否改动了,只要这一行被标记了-,此时我们就认为这一行需要重新测试覆盖,因为我们不能确定他的内容改没改
b:在git中-,+相连时的含义
如果一行代码被修改,那么-号后边一定会跟着+号,且在git log文件中,两行相连
如果是范围代码修改存在两种情况:
-比+少,意味着原代码标记-的行都已被修改,多余的+代表新增的行
-比+多,意味着有的行被修改,多余的-代表的是删除的行,连减时,后边的+需要从连减的初始行与之进行匹配,直到-不够或+不够,多余的代表增加或者减少
上述规则中b的概念比较抽象,我们在这里给大家举例帮助大家理解
eg1:下图中第6-13行(图中的行即--++++++那里)代表的就是,原文件中的第388,389两行被修改了,并在389行下面又新增了4行,这里如果大家有兴趣可以通过git diff去比较,然后手动计算验证一遍
@@ -384,9 +397,14 @@ def executeIII(commitid,branch,moduleid):
a = content.encode('unicode-escape').decode('string_escape').encode('utf-8')
contentlist = a.split("\n")
newlinenum = oldline2newlineNew(contentlist, i)
+ log.info('newlinenumis :%s' % newlinenum)
mapcoveragelinelist.append(newlinenum)
- coverage = ','.join(mapcoveragelinelist)
- tempdict1['coveragedline']=coverage
+ #异常兼容处理,如果程序正常,一定可以找到提交后对应的行
+ if len(mapcoveragelinelist):
+ coverage = ','.join(mapcoveragelinelist)
+ tempdict1['coveragedline']=coverage
+ else:
+ tempdict1['coveragedline']=[]
gapdiffresult.append(tempdict1)
continue
eg2:下图中第6-13行(图中的行即------++那里)代表的就是,原文件中的第402行,403行修改了,并删除了下面的4行,402,403,对应到新文件中即对应第388,389行
@@ -397,14 +384,9 @@ def executeIII(commitid,branch,moduleid):
a = content.encode('unicode-escape').decode('string_escape').encode('utf-8')
contentlist = a.split("\n")
newlinenum = oldline2newlineNew(contentlist, i)
- log.info('newlinenumis :%s' % newlinenum)
mapcoveragelinelist.append(newlinenum)
- #异常兼容处理,如果程序正常,一定可以找到提交后对应的行
- if len(mapcoveragelinelist):
- coverage = ','.join(mapcoveragelinelist)
- tempdict1['coveragedline']=coverage
- else:
- tempdict1['coveragedline']=[]
+ coverage = ','.join(mapcoveragelinelist)
+ tempdict1['coveragedline']=coverage
gapdiffresult.append(tempdict1)
continue
是的,你没看错,我只是把两次提交互换了一下,通过这里的学习,我们可以知道如何去判断一个行是否被改动了,或者删除了,或者它的行号发生变化了
到此为止,我们已经了解了增量计算的基本原理,接下来,我将增量计算分为三种模式,分别对应业务测试中的三种情况
1,first:即开发提交后,我们第一次进行测试,我们可以将对应的覆盖率存到数据库中,作为历史覆盖率
2,repeat:即重复测试,是指我们第二次服务部署时,代码没有变动,与上次提交代码一致,此时的覆盖率应该加上上一次的覆盖率,两处覆盖率合并得出总覆盖率
3,gap:即间隔测试,指的是我们第二次服务部署时,代码又有了改动,比如RD修复BUG,我们此时要回归验证,此时的覆盖率应该=本次覆盖率+历史不受影响代码覆盖率
此处,我们不妨通过集合概念来理解