【问题标题】:Improve Lombok @Data Code Coverage提高 Lombok @Data 代码覆盖率
【发布时间】:2017-11-18 22:48:12
【问题描述】:

我正在为我的项目编写单元测试,并试图实现至少 80% 的代码覆盖率。问题是我正在使用 lombok 的 @Data 注释来生成 getter 和 setter,当我运行单元测试时,所有这些 getter 和 setter 以及其他方法(如 toStringequalshashcode 等)都被遗漏了,我的代码覆盖率受到打击。是否有任何解决方法。我一直在搜索这方面的内容,但找不到任何可以提供帮助的东西。对此的任何帮助将不胜感激。

我正在使用 Eclemma 进行代码覆盖率分析。

【问题讨论】:

  • 正如 Nico Van Belle 所说的单元测试类不是为代码覆盖而编写的......主要目的应该是验证单元......稍后如果存在一些问题,这些类应该帮助他们找到它。 @NicoVanBelle lombok 还不错..:p :)
  • @NicoVanBelle 我明白你的意思。我只是提到我的目标是大约 80% 的代码覆盖率,但这不是我编写测试用例的原因。目的是独立测试不同的单元。
  • @VarunSharma 我认为这意味着您的课程没有被覆盖。您是否更改了 MODEL_PACKAGE 常量以引用您的包?
  • @Akshay 我不明白你所说的 MODEL_PACKAGE 是什么意思

标签: java eclipse unit-testing code-coverage eclemma


【解决方案1】:

首先,@Data注解是结合 @ToString@EqualsAndHashCode@Getter@Setter

如果你只需要 Lombok 自动创建 getter 和 setter,你可以只使用 @Getter@Setter 注释而不是 @Data .

此外,要使 Lombok 创建的方法不在此范围内,您可以在根目录中创建一个 lombok.config 文件,并包含以下两行:

config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true

添加此行后,当您转到 Sonar 时,您会看到这些类被 100% 覆盖。

【讨论】:

  • 添加这个实际上降低了我的代码覆盖率。知道为什么吗?
  • 模型类的覆盖范围是什么,@Heisenberg 的新覆盖范围是什么
【解决方案2】:

当需要 equals 和 hashcode 时,可以使用 EqualsVerifier 对它们进行非常彻底的单元测试。 EqualsVerifier 是一个开源 JUnit 库,可为 equals 和 hashCode 合约的所有部分生成单元测试,即使手动编写测试也无法直接实现。

示例用法:

@Test
public void equalsContract() {
    EqualsVerifier.forClass( MyAwesomeLombokedDataClass.class )
        .suppress( Warning.STRICT_INHERITANCE )
        .verify();
}

【讨论】:

    【解决方案3】:

    0.8.0 release 中,Jacoco 添加了对从报告中过滤出所有带有@lombok.Generated 注释的方法的支持。您唯一需要更改的是使用以下设置将lombok.config 添加到项目的根目录:

    config.stopBubbling = true
    lombok.addLombokGeneratedAnnotation = true
    
    • config.stopBubbling = true 告诉 Lombok 这是你的根 目录,并且它不应该在父目录中搜索更多 配置文件(您可以拥有多个 lombok 配置文件 在不同的目录/包中)。
    • lombok.addLombokGeneratedAnnotation = true 将添加 @lombok.Generated 对所有 Lombok 生成的方法进行注释。

    就是这样。 Jacoco 会过滤 Lombok 自动生成的方法,如果你尽力而为,你的代码覆盖率可能接近 100% :))

    【讨论】:

    • 这行得通,这实际上将 lombok 的 getter 和 setter 排除在 clover 测试覆盖范围之外
    【解决方案4】:

    你可以试试EqualsVerifier
    如果您想手动尝试,这可能会有所帮助(我的学习笔记)。这也涵盖了 96% 的突变测试:

    @Getter
    @Setter
    @Builder
    @ToString
    @Component
    @EqualsAndHashCode
    @Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
    public class ValidationEntity {
        private boolean valid;
        private int lineNumber;
        private int columnNumber;
        private String inputMessage;
        private String validationMessage;
    
        @JsonCreator
        ValidationEntity(@JsonProperty("valid") final boolean valid, @JsonProperty("lineNumber") final int lineNumber,
                         @JsonProperty("columnNumber") final int columnNumber, @JsonProperty("inputMessage") final String inputMessage,
                         @JsonProperty("validationMessage") final String validationMessage) {
            this.valid = valid;
            this.lineNumber = lineNumber;
            this.columnNumber = columnNumber;
            this.inputMessage = inputMessage;
            this.validationMessage = validationMessage;
        }
    
        public static ValidationEntityBuilder builder(String inputMessage) {
            return new ValidationEntityBuilder().inputMessage(inputMessage);
        }
    }
    

    这是我的测试类:

    /**
     * Validation Response Entity has the response details for all configured editor methods.
     * Tests are based on:
     * https://www.artima.com/lejava/articles/equality.html
     */
    public class ValidationEntityTest {
    
        @Test
        public void newValidationEntityWithBuilder_whenEqual_ThenTrue() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity z = x;
    
            //reflexive: for any non-null value x, the expression x.equals(x) should return true.
            Assert.assertTrue(x.equals(x));
            //symmetric: for any non-null values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
            Assert.assertTrue(x.equals(y) && y.equals(x));
            //transitive: for any non-null values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
            Assert.assertTrue(x.equals(z) && y.equals(z) && x.equals(y));
            //consistent: for any non-null values x and y, multiple invocations of x.equals(y) should consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
            Assert.assertTrue(x.equals(y) && x.equals(y) && x.equals(y) && x.equals(y));
            //Null check: for any non-null value x, x.equals(null) should return false.
            Assert.assertFalse(x.equals(null));
    
            Assert.assertEquals(x, y);
            Assert.assertEquals(x.hashCode(), y.hashCode());
            Assert.assertEquals(x.toString(), y.toString());
            Assert.assertEquals(x.getInputMessage(), y.getInputMessage());
            Assert.assertEquals(x.getValidationMessage(), y.getValidationMessage());
            Assert.assertEquals(x.getLineNumber(), y.getLineNumber());
            Assert.assertEquals(x.getColumnNumber(), y.getColumnNumber());
            Assert.assertEquals(x.isValid(), y.isValid());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenNotEqual_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder("Custom Input Message").lineNumber(1).columnNumber(2).valid(false).validationMessage("Custom Validation Message").build();
    
            Assert.assertFalse(x.equals(y) && y.equals(x));
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertNotEquals(x.toString(), y.toString());
            Assert.assertNotEquals(x.getInputMessage(), y.getInputMessage());
            Assert.assertNotEquals(x.getValidationMessage(), y.getValidationMessage());
            Assert.assertNotEquals(x.getLineNumber(), y.getLineNumber());
            Assert.assertNotEquals(x.getColumnNumber(), y.getColumnNumber());
            Assert.assertNotEquals(x.isValid(), y.isValid());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenBothAreEqualAndObjectInstances_ThenTrue() {
            Object x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            Object y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
    
            Assert.assertTrue(x.equals(y) && y.equals(x));
            Assert.assertEquals(x.hashCode(), y.hashCode());
            Assert.assertEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityStringWithBuilder_whenSameInstanceToStringsAreCompared_ThenTrue() {
            String x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").toString();
            String y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").toString();
    
            Assert.assertEquals("ValidationEntity.ValidationEntityBuilder(valid=true, lineNumber=11, columnNumber=22, inputMessage=inputMessage, validationMessage=Validation Successful)", x);
            Assert.assertEquals("ValidationEntity.ValidationEntityBuilder(valid=true, lineNumber=11, columnNumber=22, inputMessage=inputMessage, validationMessage=Validation Successful)", y);
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenOneIsInstanceAndOtherString_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            String y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build().toString();
    
            Assert.assertFalse(x.equals(y));
            Assert.assertFalse(y.equals(x));
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenOneIsInstanceAndOtherOneIsAChild_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
    
            class ValidationEntityExtended extends ValidationEntity {
                String additionalDetail;
    
                public String getAdditionalDetail() {
                    return this.additionalDetail;
                }
    
                public void setAdditionalDetail(final String additionalDetail) {
                    this.additionalDetail = additionalDetail;
                }
    
                ValidationEntityExtended(@JsonProperty("valid") final boolean valid, @JsonProperty("lineNumber") final int lineNumber,
                                         @JsonProperty("columnNumber") final int columnNumber, @JsonProperty("inputMessage") final String inputMessage,
                                         @JsonProperty("validationMessage") final String validationMessage, String additionalDetail) {
                    super(true, 11, 22, "inputMessage", "Validation Successful");
                    this.additionalDetail = additionalDetail;
                }
    
                @Override
                public boolean equals(Object other) {
                    boolean result = false;
                    if (other instanceof ValidationEntityExtended) {
                        ValidationEntityExtended that = (ValidationEntityExtended) other;
                        result = (that.canEqual(this) && this.additionalDetail.equals(that.additionalDetail) && super.equals(that));
                    }
                    return result;
                }
    
                @Override
                public int hashCode() {
                    return (41 * super.hashCode() + additionalDetail.hashCode());
                }
    
                @Override
                public boolean canEqual(Object other) {
                    return (other instanceof ValidationEntityExtended);
                }
    
                @Override
                public String toString() {
                    return "ValidationEntityExtended(valid=" + this.isValid() + ", lineNumber=" + this.getLineNumber() + ", columnNumber=" + this.getColumnNumber() + ", inputMessage=" + this.getInputMessage() + ", validationMessage=" + this.getValidationMessage() + ", additionalDetail=" + this.getAdditionalDetail() + ")";
                }
            }
            ValidationEntityExtended validationEntityExtended = new ValidationEntityExtended(true, 11, 22, "inputMessage", "Validation Successful", "additionalDetail");
            Assert.assertFalse(x.equals(validationEntityExtended));
            Assert.assertFalse(validationEntityExtended.equals(x));
            Assert.assertNotEquals(x.hashCode(), validationEntityExtended.hashCode());
            Assert.assertNotEquals(x.toString(), validationEntityExtended.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenEachMethodIsEqual_ThenTrue() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
    
            Assert.assertTrue(x.equals(y) && y.equals(x));
            Assert.assertEquals(x.hashCode(), y.hashCode());
            Assert.assertEquals(x.toString(), y.toString());
            Assert.assertEquals(x.getInputMessage(), y.getInputMessage());
            Assert.assertEquals(x.getValidationMessage(), y.getValidationMessage());
            Assert.assertEquals(x.getLineNumber(), y.getLineNumber());
            Assert.assertEquals(x.getColumnNumber(), y.getColumnNumber());
            Assert.assertEquals(x.isValid(), y.isValid());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenInputMessageNullForOne_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder(null).lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
    
            Assert.assertNotEquals(x, y);
            Assert.assertNotEquals(y, x);
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertNotEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenInputMessageNullForBoth_ThenTrue() {
            ValidationEntity x = ValidationEntity.builder(null).lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder(null).lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
    
            Assert.assertEquals(x, y);
            Assert.assertEquals(y, x);
            Assert.assertEquals(x.hashCode(), y.hashCode());
            Assert.assertEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenInputMessageNotEqual_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Custom Input Message").build();
    
            Assert.assertFalse(x.equals(y) && y.equals(x));
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertNotEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenValidNotEqual_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            y.setValid(false);
    
            Assert.assertFalse(x.equals(y) && y.equals(x));
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertNotEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenLineNumberNotEqual_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            y.setLineNumber(1);
    
            Assert.assertFalse(x.equals(y) && y.equals(x));
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertNotEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenColumnNumberNotEqual_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            y.setColumnNumber(2);
    
            Assert.assertFalse(x.equals(y) && y.equals(x));
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertNotEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenValidationMessageNullForOne_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage(null).build();
    
            Assert.assertNotEquals(x, y);
            Assert.assertNotEquals(y, x);
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertNotEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenValidationMessageBothNull_ThenTrue() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage(null).build();
            ValidationEntity y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage(null).build();
    
            Assert.assertEquals(x, y);
            Assert.assertEquals(y, x);
            Assert.assertEquals(x.hashCode(), y.hashCode());
            Assert.assertEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithBuilder_whenValidationMessageNotEqual_ThenFalse() {
            ValidationEntity x = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Validation Successful").build();
            ValidationEntity y = ValidationEntity.builder("inputMessage").lineNumber(11).columnNumber(22).valid(true).validationMessage("Custom Validation Message").build();
    
            Assert.assertFalse(x.equals(y) && y.equals(x));
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertNotEquals(x.toString(), y.toString());
        }
    
        @Test
        public void newValidationEntityWithAllArgsConstructor_whenEqual_ThenTrue() {
            ValidationEntity x = new ValidationEntity(true, 11, 22, "inputMessage", "Validation Successful");
            ValidationEntity y = new ValidationEntity(true, 11, 22, "inputMessage", "Validation Successful");
    
            Assert.assertTrue(x.equals(y) && y.equals(x));
            Assert.assertEquals(x.hashCode(), y.hashCode());
            Assert.assertEquals(x.toString(), y.toString());
            Assert.assertEquals(x.getInputMessage(), y.getInputMessage());
            Assert.assertEquals(x.getValidationMessage(), y.getValidationMessage());
            Assert.assertEquals(x.getLineNumber(), y.getLineNumber());
            Assert.assertEquals(x.getColumnNumber(), y.getColumnNumber());
            Assert.assertEquals(x.isValid(), y.isValid());
        }
    
    
        @Test
        public void newValidationEntityWithAllArgsConstructor_whenNotEqual_ThenFalse() {
            ValidationEntity x = new ValidationEntity(true, 11, 22, "inputMessage", "Validation Successful");
            ValidationEntity y = new ValidationEntity(false, 1, 1, "Custom inputMessage", "Custom Validation Successful");
    
            Assert.assertFalse(x.equals(y) && y.equals(x));
            Assert.assertNotEquals(x.hashCode(), y.hashCode());
            Assert.assertNotEquals(x.toString(), y.toString());
            Assert.assertNotEquals(x.getInputMessage(), y.getInputMessage());
            Assert.assertNotEquals(x.getValidationMessage(), y.getValidationMessage());
            Assert.assertNotEquals(x.getLineNumber(), y.getLineNumber());
            Assert.assertNotEquals(x.getColumnNumber(), y.getColumnNumber());
            Assert.assertNotEquals(x.isValid(), y.isValid());
        }
    }
    

    【讨论】:

    • 大量注释......有时我想知道,以可读性的名义,它们增加了复杂性......整个目的都被兰博克打败了
    • @Stunner 堆栈在元注释@Data 中进行了总结,这大大简化了事情。它还使源代码尽可能少,而不是在业务逻辑中使用数百个 getter 和 setter。
    【解决方案5】:

    在根级别创建一个 lombok.config 文件并添加此代码

    config.stopBubbling = true
    lombok.addLombokGeneratedAnnotation = true
    

    会被代码覆盖率忽略

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-16
      • 2020-02-20
      • 1970-01-01
      • 2021-07-29
      • 1970-01-01
      • 2012-06-30
      • 2023-04-09
      • 2018-01-11
      相关资源
      最近更新 更多