摘要:随着金融行业信息化程度的不断提高,银行业务系统面临着前所未有的复杂性和安全性挑战。为了确保系统稳定运行,银行必须在软件开发生命周期中实施严格的测试流程。本文将探讨如何基于Jacoco框架进行二次开发,从而实现银行交易的精准测试工作。通过这种方式,不仅可以提高测试可信度和测试充分性、还能协助定位缺陷、识别冗余代码,进而保证金融服务的质量与安全。
一、背景介绍
在银行业务核心系统中,程序类型分为两类,联机交易和批量交易。联机交易一般都是供其他系统或者柜面前台调用的接口,批量交易则往往是日间或者日终银行跑批用的,用来集中处理账务加工、计提结息、账务核对、监管报送等内容。银行系统对于测试质量要求很严格,一个小的记账缺陷,就可能会对客户或银行都会造成巨大的损失,因此引入精准测试工具对于确保银行测试质量非常关键。银行系统通常涉及大量的业务逻辑和复杂的交易流程,传统的黑盒测试方法一般只能基于需求编写执行案例,通过评审或经验判定测试用例覆盖需求的程度,而测试用例具体覆盖了多少程序或代码,并没有量化数据。为了提高测试可信度和充分性,引入代码级测试工具(如JaCoCo)并进行适当的二次开发,对于实现精确的系统测试至关重要。同时通过引入ASM技术,还可以进一步分析变更代码的测试范围、 未覆盖代码的影响范围并反馈测试人员,从而形成一个精准测试的闭环。
二、精准测试工具简介
精准测试工具中具体代码覆盖工具通常使用代码插桩技术来实现,根据插桩对象不同分为源代码插桩、中间代码插桩、二进制代码插桩三大类。业内主要的代码覆盖工具有Emma,Cobertura,Jacoco,Clover等。
Jacoco是一个开源的Java代码覆盖率检测工具,使用中间代码插桩方式,采用字节码注入的方式,使用ASM字节码框架技术在原有的class字节码中插入探针代码,形成新的字节码指令流,通过统计运行时探针的覆盖情况来统计覆盖信息。因Jacoco是对Java编译后生成的class文件进行插桩,因此对源代码不可见也没有侵入性修改。Jacoco报告支持多种格式包括html,xml,csv等,它使用方法也很灵活可以集成到常见的构建工具(如Maven、Gradle)中,也支持Eclipse,IDEA等IDE工具,可以帮助开发以及测试人员轻松获取单元测试、集成测试、系统测试等各种类型测试的代码覆盖率报告。通过JaCoCo插件,我们可以监控到测试执行期间程序代码的执行情况,进而分析哪些部分得到了充分覆盖,哪些部分还需要进一步加强测试。
Diff-cover是一个开源的使用python编写的增量代码覆盖工具,此工具可以结合Jacoco,Clover,Cobertura等全量报告生成工具来使用,在系统全量报告的基础上,进一步生成两个版本之间增量(或变更)代码的增量报告。实际测试过程中,一个系统的代码往往通过不同的项目不断地迭代,对于系统测试人员来说,更关注变更代码的覆盖率。这个时候就需要在Jacoco工具的基础上引入新的增量代码覆盖工具。
三、基于JaCoCo的精准测试实践
1. 精准测试工具优化
1)生成报告工具优化
众所周知,Jacoco是一个全量代码覆盖工具,它生成的代码覆盖报告是针对整个系统的全量代码的。Diff-cover是一个比较流行的增量代码覆盖工具,它必须要配合全量代码覆盖工具来使用,它是在全量代码覆盖报告基础上,通过git diff来获取变更代码,进而生成增量代码覆盖报告。但是在实际实践过程中,Diff-cover仍然有他的局限性,Jacoco与Diff-Cover的对比如下表所示,例如diff-cover的统计维度单一,覆盖标识信息少了部分覆盖的情况,这样对于分支代码部分覆盖的信息就丢失了。
因此在实践过程中,我们前期使用的是Jacoco+Diff-cover开展。后期在Jacoco工具的基础上,借鉴Diff-cover等开源工具的思想,二次定制开发了一个小的增量代码覆盖报告工具,替换了diff-cover,在统计维度上保留了行、 分支、 类、 方法覆盖率。覆盖标识上复用全量报告的三种覆盖标识,完全覆盖、部分覆盖、未覆盖。具体通过使用org.eclipse.jgit比较新旧分支代码差异,识别变更行信息,生成报告时在全量覆盖报告的变更行信息前面打标或高亮显示,未检出变更的代码行不做处理。同时在Jacoco的代码覆盖报告首页增加增量代码覆盖数据统计表、增量代码比对版本等信息,格式参照全量代码覆盖报告的首页格式。这样全量和增量代码覆盖信息就可以集成到同一份代码覆盖报告里了。
在部署完成jacoco后,可以随时生成jacoco.exec并下载,然后基于分支差异情况解析生成报告。
2)报告合并工具优化
Jacoco工具可以获取系统代码的全量覆盖率,而且对于单元测试非常友好,因为单元测试案例较简单,往往可以一次性自动化地执行全量案例。但是系统测试不同,系统测试的案例复杂,关联系统和软件较多,且自动化水平根据系统复杂度以及测试案例复杂度层次不齐,此外在实际测试过程中,系统往往需要随着项目版本迭代来修复问题,或者逐渐完成不同需求点的开发,因此涉及到了一个比较重要的问题——代码覆盖报告的合并。Jacoco工具虽然支持代码覆盖报告的合并,但是合并过程中有一个问题,如果一个Java类代码在下一个版本中发生了变化,哪怕只是修改了一行或者修改了一个编码规范问题,对于Jacoco来说,它会认为这已经是两个不同的class了,那么在合并报告的过程中,这个class的覆盖率数据就会清零。
这个问题对于系统测试的精准测试实践来说非常重要,它很大程度上影响了系统测试代码覆盖报告的准确度,特别是对于发版频繁的项目来说。比如测试人员有一个5天需要测试的功能,写了80条案例,前4天已经测完60条案例了,第4天发现一个缺陷,这个时候变更代码的覆盖率已经达到了80%。然后开发为了修复bug修改了一行代码并重新部署,绝大部分情况下第5天测试人员只需要复测与修改代码相关的测试案例,然后把剩余20条案例测试完成即可,但不幸的是,修复bug的部分与前4天测试的内容是同一个class,该class往往可能包含多个方法,修复bug的改动代码可能只是其中某一个方法的一行变量赋值的代码,这种情况下生成的增量代码覆盖率只有后面20条案例执行的代码覆盖数据。前面覆盖的80%的数据已经丢失了。因此我们对报告合并功能进行了优化,实现思路是对同一个class文件有代码发生变化的情况下会进一步通过代码识别发生变化的代码所在的方法,对于其他不包含优化代码的方法进行覆盖率数据合并,对于存在代码变化的方法进行覆盖率清零。这样可以进一步提高报告合并后的代码覆盖数据的准确率。
3)报告分析工具优化
在实践过程中使用了基于ASM技术开发出的静态代码调用链路分析工具,可以通过分析代码覆盖报告中的未覆盖代码分析需要补充测试案例的交易。这个在目前已经有不少成熟的开源代码可以借鉴。
2. 实施精准测试
实施和推广精准测试的关键在于真正的为提高测试质量赋能同时降低使用门槛。
首先,要达到为提高测试质量赋能的目的,就需要让系统测试的代码覆盖报告数据尽可能准确,针对每一次迭代版本的系统测试覆盖率是准确的,对于多版本迭代的情况,如果出现前面所述的修改类的覆盖数据合并的情况则会对整体覆盖率有影响,整体会偏低一些。针对这部分问题,使用报告合并工具优化章节中提到的方法进行二次开发就可以缓解这一问题。因此默认自动生成最近一次迭代版本的覆盖率报告,同时支持多个迭代版本报告的合并,对于多集群部署,支持不同集群覆盖报告的合并。
其次是降低使用门槛,本身部署Jacoco以及增量代码覆盖工具,收集exec文件以及生成报告对于非技术背景的测试人员来说是比较困难的,即使对于技术背景的测试人员,在项目周期非常紧张的时候使用意愿也会大大降低。因此精准测试工具引入之后整体流程的自动化尤为重要。
1)准备工作
首先对于多个试点系统来说,需要申请专门的机器资源,存储资源,以免资源不够或争抢导致大家无法使用。其次针对前面提到的降低使用门槛的问题,我们需要把整个流程自动化,尽可能减少人为操作,提高使用体验。因此我们需要借助流水线技术,也便于无缝集成到系统的CI/CD或TEST流水线上。
具体来说,采取了以下4个步骤:
①申请资源:申请运行流水线的后台构建机、用于文件传递的NAS资源、报告发布服务器、数据库等。
②环境搭建:在构建机及服务器上安全部署必要的软件以及Jacoco、 增量代码覆盖工具包等。
③方案设计与实现:使用Jekins或其他流水线编排工具进行流水线编排,分别进行从代码库源代码拉取、从测试环境进行exec文件以及应用程序包war/jar等拉取、调用Jacoco命令生成全量报告、调用二次开发的增量代码覆盖工具在全量报告上添加增量代码覆盖统计数据以及覆盖行进行打标操作等。此外,此流水线应作为每次开发人员向测试环境发版的流水线的一个自动出发的子流水线或步骤,这样保证每次新的迭代版本发布之前都会自动生成上一个版本的系统测试代码覆盖报告。
④人员培训:编写手册,并组织有试用意向的系统的相关开发测试联系人开展培训。
后续各个系统就可以开始实施了。
2)系统实施
准备工作结束后,精准测试工具在各个系统的部署和使用就可以开始了。具体如下:
①各系统部署Jacoco:首先在各个系统测试环境进行一次性部署工作。Jacoco插桩工作,同时在应用服务器启停脚本中配置jacoco的dump操作来保证每次重启服务器覆盖率数据exec文件都能dump下来不丢失。
②各系统引入流水线:在各个系统的CI/CD或者TEST流水线中引入前面实现的精准测试流水线作为一个子流程,这样在每次发版前就可以自动生成上一个版本的代码覆盖报告。
③各系统生成报告:前两个步骤针对各个系统是一次性的工作,完成后无需再重复执行。后面测试人员就可以开展系统测试了,在测试过程中,开发或测试人员一旦进行新的迭代版本在测试环境的发版操作,精准测试流水线会被自动触发,并自动生成全量及增量的代码覆盖报告,并邮件发送给测试人员报告链接,测试人员通过点击链接可以直接查看发布到报告服务器上的报告。
④其他:在流水线生成和发布报告的过程中同时将报告相关各个维度的覆盖数据,版本号,时间戳,系统名称等信息同步录入数据库,便于使用历史数据进一步分析生成覆盖率折线图等信息提供给测试人员。
四、案例实践
某银行在其多个系统升级改造过程中采用了基于Jacoco的精准测试方案。通过前期的准备工作,包括工具选型、资源申请、环境搭建、方案部署、人员培训等,最终实现了对多个重要系统的推广覆盖。据统计,到目前为止参与系统23个,整体平均增量行覆盖率暂为66%,根据代码覆盖报告中的未覆盖代码进行分析,补充有效案例1933条,部分项目仍在测试中。通过引入精准测试技术,大大提升了系统的稳定性和安全性。
五、结论
代码覆盖率的高低是反映测试案例执行率的高低,而测试案例是测试人员在解析业务需求、代码设计分析等相关文档后设计出来的,因此代码覆盖率的高低一定程度反应了业务需求实现的情况,但并不是完全一致的。100%的代码覆盖率并不代表需求被100%覆盖。
有三种情况需要说明:
一、需求与已覆盖代码有很好的对应关系,需求达到了被完全覆盖,这是一种比较理想的情况。
二、需求的范围大于已覆盖的内容,这种情况下,在代码覆盖报告上往往还会看到未覆盖代码,这种情况属于案例对需求覆盖不全,测试人员往往需要补充案例来达到完整覆盖需求的目标。
三、需求的范围小于已覆盖代码的内容,发生这种情况的起因是需求的范围小于增量/变更代码的范围。也就是前面提到的开发人员进行了“超前开发”,但测试人员为了完整地测试到变更代码对“超前代码”也进行了覆盖。这种情况一般要保证需求与变更代码一致。因此这种情况要建议开发人员进一步完善需求文档,或者去掉“超前”开发内容,以免造成安全隐患。
代码覆盖率中,已覆盖代码比率是反应执行案例的结果,该覆盖率与执行案例是对应的。未覆盖代码比率反映的是没有执行到的代码,这部分代码有些是需要进一步补充或执行更多的案例才能覆盖。在已覆盖代码中,如果程序发生了与预期不一样的结果或异常,就是测试所发现的缺陷,因此缺陷对应的往往是已覆盖的代码。在开发与测试人员定位缺陷的时候,可以借助已覆盖代码来进行缺陷的进一步定位和分析。
综上所述,基于Jacoco的二次开发不仅能够有效提高银行系统交易测试的充分性、可信度,提高代码质量,还能帮助团队建立起一套标准化、自动化的测试流程。随着技术的不断进步,相信未来还会有更多创新性的测试方法涌现出来,为银行的测试工作带来更加高效可靠的解决方案。
作者:赵莉