JaCoCo增量覆盖率的基本实现原理

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 什么是增量覆盖率 如图所示,在master分支提交了HelloController,然后从master拉了个新分支test;提交了第1次代码,增加了WorldController;提交了第2次代码,增加了DonController。增量的获取方式有两种: 版本对比:在分支上,第2次提交和第1次提交版本对比的增量代码,就是DonController;第2次提交和刚从master拉取分支时版本对比的增量代码,除了DonController还有WorldController; 分支对比:把test分支和master分支进行对比,增量代码就是DonController和WorldController; 增量,就是通过版本对比或分支对比,多出来的新增加的那部分代码。覆盖率是指程序运行以后,有多少代码被执行到了,除以总的代码数算出来的,即覆盖率=执行代码行数/总代码行数。增量覆盖率是针对增量代码来计算的,也就是增量覆盖率=执行代码行数/增量代码行数。 基本实现原理 使用JaCoCo的On-the-fly模式,以tcpserver方式启动,将远程机器的覆盖率数据通过TCP通信dump到本地jacoco.exec文件,二次开发JaCoCo源码,实现增量覆盖率分析和输出HTML报告。 JaCoCo默认覆盖率 JaCoCo是一个开源的覆盖率工具,它的On-the-fly模式,无须侵入应用启动脚本,只需在JVM中通过-javaagent参数指定jar文件启动的代理程序,代理程序在ClassLoader装载一个class前判断是否需要注入class文件,将统计代码插入class,覆盖率分析就可以在JVM执行的过程中完成。 启动脚本示例: java -javaagent:jacocoagent.jar -jar target/app.jar 默认情况下,JaCoCo会在JVM停掉以后,生成覆盖率数据,一个jacoco.exec文件。 官方手册也有相关说明: output默认为file:At VM termination execution data is written to the file specified in the destfile attribute。这种方式有2个局限,一是必须停掉应用;二是只能把覆盖率数据生成到本地文件。 output还有另外一个选项,tcpserver: java -javaagent:jacocoagent.jar=output=tcpserver,address=0.0.0.0,port=2014 -jar target/app.jar address:服务端地址; port:服务端端口; 通过这种方式启动后,会同时启动一个TCP服务端,使用lsof -i :2014命令查看端口进程: 然后就能通过TCP通信,来获取覆盖率数据,也就是所谓的”dump“。 代码片段: @Override public void dump(String dumpPath, String ip, int port) throws IOException { dumpPath = FileUtil.getAbsolutePath(dumpPath); if (!FileUtil.exist(dumpPath)) { FileUtil.mkdir(dumpPath); } FileOutputStream localFile = new FileOutputStream(dumpPath + File.separator + "jacoco.exec"); ExecutionDataWriter localWriter = new ExecutionDataWriter(localFile); SocketAddress socketAddress = new InetSocketAddress(ip, port); Socket socket = new Socket(); try { socket.connect(socketAddress, 10000); RemoteControlWriter writer = new RemoteControlWriter(socket.getOutputStream()); RemoteControlReader reader = new RemoteControlReader(socket.getInputStream()); reader.setSessionInfoVisitor(localWriter); reader.setExecutionDataVisitor(localWriter); log.info("开始dump:{} {}", ip, port); writer.visitDumpCommand(true, false); if (!reader.read()) { log.error("Socket closed unexpectedly"); throw new IOException("Socket closed unexpectedly."); } log.info("dump完成:{} {}", ip, port); } catch (Exception e) { log.info("dump失败:{}", e.getMessage()); } finally { socket.close(); localFile.close(); } } dump经过TCP通信,将远程机器的覆盖率数据取到本地后,生成jacoco.exec文件,然后可以使用jacococli.jar生成HTML报告: ...

SpringBoot接入JaCoCo实践教程

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 静态接入和动态接入 SpingBoot可以通过2种方式接入JaCoCo:Maven和Agent。Maven方式是静态接入,在编译时计算代码覆盖率。Agent方式是动态接入,服务启起来以后,能实时根据代码命中情况计算代码覆盖率。 教程代码已上传开源项目:https://github.com/dongfanger/mini Maven pom.xml添加依赖: <dependency> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.7</version> </dependency> 添加配置: <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${project.parent.version}</version> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.7</version> <executions> <execution> <id>default-prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> </execution> <execution> <id>default-report</id> <goals> <goal>report</goal> </goals> </execution> </executions> </plugin> </plugins> </build> 点击maven的install: target/site/jacoco目录下就生成了覆盖率报告: Agent 下载jacoco文件后解压,提取2个文件: 使用命令启动SpringBoot,启动参数添加-javaagent: java -javaagent:jacoco/jacocoagent.jar -jar target/mini-0.0.1-SNAPSHOT.jar 此时会生成1个jacoco.exec文件,这个文件就是覆盖率数据文件,采用以下命令根据数据文件生成HTML报告: java -jar jacoco/jacococli.jar report jacoco.exec --classfiles ./target/classes/ --sourcefiles ./src/main/java/ --html ./report 接下来就来验证动态特性。把report目录和jacoco.exec都删了,因为jacoco每次部署只能分析一次。把服务停掉后,重新启动。这一次不直接分析,而是先用postman调一下接口: 然后再分析一把: ...

代码覆盖率从0到100的入门实践

JaCoCo全称是Java Code Coverage,Java代码覆盖率,广泛运用于各种测试平台对Java代码的全量覆盖率和增量覆盖率进行统计,分析代码行差异,度量单元测试效果。Jacoco也是精准测试的技术实现手段之一。

代码评审,揭示黑盒背后的真相

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 一、引言 黑盒测试犹如案发现场,只能根据表象推断事件经过。 代码评审即深入调查,挖掘蛛丝马迹的线索,揭示背后的真相。 “They think I am hiding in the shadows, but I am the shadows.” 二、黑盒测试与白盒测试的区别 黑盒测试存在一些局限性: 可能无法发现与系统实现相关的问题 可能无法覆盖所有的测试场景 测试效率较低,比如准备物料、模拟场景 强依赖需求文档,如果文档不全,测试会漏 对于测试人员来说,可以在代码评审阶段,通过白盒测试改进测试的质量和效率。 三、代码评审的定义和意义 代码评审,Code Review(CR),是一种通过检查代码来提高代码质量的过程。 对于测试人员来说,参与代码评审,可以尽量提前发现问题,减少修复代价,提高效能。 四、代码评审的形式 多人讨论 组织会议,研发牵头讲解代码,架构和测试参与,讨论交流。这是最普遍的一种形式。 Code Diff 查看Code Diff,可以借助Gitlab或IDEA,比较分支差异或版本差异。 对比时机: 提测前和测试中,自行走查代码 发现缺陷,定位代码原因 修复缺陷后,评估影响范围 上线前,是否夹带代码 精准测试 评估测试用例的代码覆盖率,查漏补缺,Jacoco的on-the-fly模式支持动态收集代码覆盖率数据。 五、代码评审的方法 面向业务,面向业务,面向业务。重要的事情说三遍。 刚开始做代码评审,很容易把注意力集中在找代码规范问题上面,比如命名不规范、注释不清楚、代码实现冗长等。这些问题不是测试人员关注的重点,需要由研发团队或代码扫描工具来解决。 在做Code Diff时,也没必要把每个文件、每行代码的意思搞懂,比如研发对代码结构做了调整,在diff时要梳理清楚的话,ROI会非常低,因为既消耗时间,又发现不了问题。 那该怎么做代码评审呢? 关注业务: 跟需求文档比较,哪些需求是遗漏的,哪些代码是补充的,哪些代码是夹带的 关注核心业务代码逻辑,使用条件覆盖、路径覆盖等方法设计测试用例 优化测试用例,针对代码实现考虑异常、边界、幂等、并发等场景 代码评审要求测试人员具备代码能力,理解编程语言,掌握软件设计,熟悉代码结构和架构,多与开发同学交流,共同优化代码质量。 六、代码评审的实际案例 1、空指针异常 import org.mybatis.spring.SqlSessionTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { private final SqlSessionTemplate sqlSessionTemplate; @Autowired public UserService(SqlSessionTemplate sqlSessionTemplate) { this.sqlSessionTemplate = sqlSessionTemplate; } public void getUserById(String userId) { User user = sqlSessionTemplate.selectOne("com.example.mapper.UserMapper.getUserById", userId); System.out.println(user.getName()); // 可能导致空指针异常 } } 如果取到的 user 对象为空,就会导致空指针异常。 ...

CodeReview技巧和规范

本书作者:测试开发刚哥,文章版权归个人所有,未经授权请勿转载。 What-什么是CodeReview? CodeReview,即代码评审,是白盒测试中,静态测试的一种方法,通过阅读代码,提出代码层面的缺陷。 Why-为什么做CodeReview? 测试左移:在测试前就发现代码缺陷。 明确测试范围:根据需求文档和代码,对齐测试范围,防止研发夹带本次需求范围外的代码上线。 提升代码能力:学习研发优秀的编程技术。 深入原理:了解系统内部结构及实现。 发现隐藏缺陷:能发现某些极端场景下的问题或概率性问题。 减少测试研发周期占比:节省用例执行个数,尤其多个同类异常梳理,不用执行类似用例,CodeReview一行代码即可。 Who-谁适合做CodeReview? CodeReview需要三大门槛: ①熟悉编程语言,Java/Python/Go等。 ②熟悉编程规范,《阿里巴巴XXX编码规范》、《华为XXX编码规范》、《京东XXX编码规范》。 ③了解需求、架构和设计,需求文档/架构图/设计文档。 达到门槛才适合做CodeReview。 When-什么时候做CodeReview? 提测前,通过Code Diff Review,自己走读代码或者参与研发代码评审会议均可, 发现功能缺陷时,通过走查代码定位代码原因,并分析是否有关联缺陷。 修复缺陷时,阅读研发commit记录,检查是否引入新缺陷。 上线前,通过merge范围检查是否有夹带代码,为了测试改动的临时代码是否恢复。 Where-在哪里做CodeReview? 线上仓库(如Gitlab),适合于修改点单一,不需要查看上下文信息就能找到缺陷。直接在网页端评审代码且发表comment。 本地仓库(如IDEA),适合于修改点较多,需要查看上下文信息(变量或方法跳转到定义或使用之处)。在本地找到缺陷后在线上发表comment。 How-CodeReview如何上手? 准备:了解需求、架构和设计;学习编程语言;学习代码规范; 实践:坚持CodeReview,记录缺陷,定期总结。如果看代码找缺陷比较困难,可以先从发现功能缺陷后,定位代码原因入手。 常见CR缺陷: 除数为0、整数溢出、精度损失; 可能死循环; 在finally程序块中关闭或者释放资源; 异常未处理或提示不明确(没有catch异常,集合等没有判空和长度为0); 公式计算错误; 字符串对比不能用==,使用equals; 数组可能越界; 传递引用错误; 类型转换错误; 条件范围选择错误; 重点检查项: 一致性检查:是否需求相关;是否和方案设计一致;代码风格、日志规范、异常处理等是否和统一规范一致; 完整性检查:服务层是否包含了所有业务功能;数据层是否包含所有需要的数据和操作; 正确性检查:计算逻辑等业务逻辑是否正确;变量是否被正确定义和使用;代码是否符合制定的标准; 可修改性检查:代码涉及到常量是否易于修改,比如使用配置,定义为常量类等;建议业务方法只有一个出口和一个入口(异常处理除外);重要公共方法是否有交叉注释说明;重要的对外方法修改后影响多个下游接口; 可预测性检查:变量初始化;方法稳定性;代码是否存在死循环;代码无穷递归检查; 健壮性检查:代码是否采取措施避免运行时错误(如数组边界溢出、被零除、值越界、堆栈溢出等) 高可用性检查:是否有预案(降级开发、限流配置、兜底策略);补偿方案是否合理; 结构性检查:程序的每个功能是否都作为一个可辨识的代码块存在; 可理解性检查:是否使用到不明确或不必要的复杂代码;代码中的算法是否符合开发文档中描述的数学模型;每个变量都定义了合法的取值范围; 可验证性检查:代码功能是否便于测试;单元测试覆盖度是否足够; 可追溯性检查:代码是否包括一个修订历史记录,记录中对代码的修改和原因都有记录; 编码规范: 《阿里巴巴Java开发手册》 最新版本:黄山版(2022.2.3发布) https://github.com/alibaba/p3c