Lombok有毒慎用?会导致覆盖率崩塌?

2021-03-10 14:50:33 浏览数 (3)

有没有发现,在引入Lombok之后,jacoco扫出来的覆盖率是不是一下子掉下来了?

Lombok 由于其使用的便利性, 目前流传非常广泛。甚至有呼声希望其能被Java官方引入,成为JDK的一部分。

当然凡事都有两面性,Lombok的引入也是有代价的。一时注释一时爽,结果导致代码在不知不觉中翻了好几倍。

例如以下几个简单的注解,背后是N多个自动生成的方法,

  • @Data注解:这是若干个注解的组合,包括有@Setter、@Getter、@ToString和@EqualsAndHashCode的功能,还会添加一个公共的构造方法。staticConstructor 参数可将构造方法置为private,并提供一个获取对象的静态方法。
  • @Builder:使用私有构造函数生成一个名为TBuilder的内部类,创建一个构造器。
  • @NoArgsConstructor:该注解使用在类上,该注解提供一个无参构造
  • @AllArgsConstructor:该注解使用在类上,该注解提供一个全参数的构造方法,默认不提供无参构造方法。

这其中尤其是@Data这个注解,会附带相当多的方法。

默认情况下,由于Jacoco不会区分Lombok生成的代码和正常的源代码。结果,在引入Lombok后就会发现,覆盖率通常会低得让人匪夷所思。例如某个项目,通过Jacoco获取到的代码行数以及覆盖率为:

可见,这个覆盖率是非常低的,会被质量门禁判违规(常见会要求>50%甚至>80%)。

这种情况下,开发者一般会有两个选择:

  1. 专门为这些生成的代码编写单元测试用例
  2. 要求降低质量门禁中的覆盖率要求

通常这两个方案都是不可取的。

专门为这些生成的代码编写用例是没有意义的。既然使用了Lombok,一个默认的前提就是Lombok是正确可靠的,为这些自动生成的代码进行单元测试不是一件高优先级的事情,还是放过已经996的码农和他们的头发吧,要爱护那些愿意写单元测试的好同志。

第二种方案也不可取,这会引入一个非常不好的开始,因为破窗效应,马上质量门禁也没有意义了。千万个教训告诉我们,千万不要去考验人性。

两种选择都没有意义,也都不可取,于是马上就有人想到了第三种方法

3 手工排除Bean

无论是Jacoco还是Sonar,都提供了exclude的方式,通过配置项来指定统计时排除某些特定的包或者类。如对于Jacoco可以在jacoco-maven-plugin的<configuration>中有如下配置,用于排除指定的内容。

代码语言:javascript复制
<configuration>
<excludes>
<exclude>**/*DTO.*</exclude>
<exclude>**/domain/**/*</exclude>
</excludes>
</configuration>

第一个<exclude>标签将吧所有DTO结尾的类排除,而第二个<exclude>标签将把domain目录和子目录下的所有类都排除出去。

通过这个方式,也可以在数据源头上进行排除。

类似的,还可以通过基于jacoco覆盖率报告来自研解析工具,进行覆盖率或者增量覆盖率计算。这个过程中,也可以指定需要排除的包或者类,实现类似的做法。

当然,这种方式也需要项目有一些项目结构和命名上的约定,以保证过滤的正确。另外,既然放开了过滤的条件,有可能会让人钻空子。

4 使用Sonarqube 而不是Jacoco的结果

虽然Jacoco中的数据受到了lombok的污染,但是SonarQube由于有自身代码行的计算是根据扫描的源码,再根据自身的算法进行计算,并不是根据Jacoco提供的数据。再由此计算覆盖率的时候,就可以部分规避掉这个问题了。所以这是一个正解。当然,由于SonarQube和Jacoco的代码行、覆盖率等算法有差异,最好是保持指标数据源前后的一致性,避免混用。

5 使用lombok.addLombokGeneratedAnnotation

其实从Jacoco 0.8.0开始,其实就具备了可以将lobmok生成的代码从Jacoco报告中排除的能力。发布说明可以参见

https://github.com/jacoco/jacoco/pull/513

具体做法是,在项目的根目录下新建一个名字为lombok.config的文件,里面有如下的内容,

代码语言:javascript复制
config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true

Lombok在为由其生成的构造方法、方法、字段和类型中增加@Generated注解,然后Jacoco借助这个注解来实现更为准去的排除。排除以后的结果如下,

当然,这个方案对两者的版本也有限制,要求Lombok >= 1.16.14, jacoco>0.8.0

本文案例图片来自: rainerhahnekamp ,案例源码可以从 GitHub 上获取。

总结一下,面对lombok,有如下五种解决方案,推荐后两种哦。

1专门为这些生成的代码编写单元测试用例 【X】

2要求降低质量门禁中的覆盖率要求【X】

3手工排除Bean 【X】

4使用Sonarqube 而不是Jacoco的结果【OK】

5使用lombok.addLombokGeneratedAnnotation 【OK】

覆盖率统计,我们是认真的

0 人点赞