Улучшить покрытие кода Lombok @Data

Я пишу модульные тесты для своего проекта и пытаюсь достичь не менее 80% покрытия кода. Проблема в том, что я использую аннотацию lombok @Data для создания геттеров и сеттеров, и когда я запускаю свои модульные тесты, все эти геттеры и сеттеры вместе с другими методами, такими как toString, equals, hashcode и т. д., пропускаются, и мое покрытие кода получает удар. Есть ли обходной путь для этого. Я много искал об этом, но не смог найти ничего, что могло бы помочь. Любая помощь в этом будет оценена по достоинству.

Я использую Eclemma для анализа покрытия кода.


person Varun Sharma    schedule 16.06.2017    source источник
comment
как Нико Ван Белль сказал, что классы модульных тестов не написаны для покрытия кода... основной целью должна быть проверка модулей... позже, если возникнут какие-то проблемы, эти классы должны помочь им найти их. @NicoVanBelle ломбок не так уж и плох ..:p :)   -  person Akshay    schedule 16.06.2017
comment
@NicoVanBelle Я понимаю твою точку зрения. Я только что упомянул, что стремлюсь к покрытию кода примерно на 80%, но это не причина, по которой я пишу тестовые примеры. Намерение состоит в том, чтобы протестировать различные устройства независимо друг от друга.   -  person Varun Sharma    schedule 16.06.2017
comment
@VarunSharma Я думаю, это означает, что ваш класс не покрывается. Вы изменили константу MODEL_PACKAGE, чтобы она ссылалась на ваш пакет?   -  person Akshay    schedule 16.06.2017
comment
@Akshay Я не понял, что ты имеешь в виду под MODEL_PACKAGE   -  person Varun Sharma    schedule 16.06.2017
comment
Добавьте строку private static final String MODEL_PACKAGE = имя вашего пакета;   -  person Akshay    schedule 16.06.2017
comment
@Akshay, ты имеешь в виду добавить его в каждый файл, где я использую аннотацию? и что это делает, если вы могли бы указать   -  person Varun Sharma    schedule 16.06.2017
comment
В вашем классе модульного тестирования просто укажите имя пакета... вы должны получить красный цвет в классе геттеров/сеттеров, поскольку он не покрывается в классе модульного теста   -  person Akshay    schedule 16.06.2017
comment
Давайте продолжим обсуждение в чате.   -  person Varun Sharma    schedule 16.06.2017
comment
См. jdev.it/tips-unit-testing-javabeans.   -  person JavaTec    schedule 19.01.2018
comment
Решение: medium.com/@mladen.bolic/   -  person Rishabh Agarwal    schedule 13.02.2019


Ответы (5)


Прежде всего, аннотация @Data представляет собой комбинацию @ToString, @EqualsAndHashCode, @Getter, >@Сеттер.

Если вам просто нужно, чтобы Lombok автоматически создавал геттеры и сеттеры, вы можете использовать только аннотации @Getter и @Setter вместо @Data.

Кроме того, чтобы сохранить методы, созданные Lombok, за пределами этого охвата, вы можете создать файл lombok.config в своем корневом каталоге и иметь следующие две строки:

config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true

После добавления этой строки, когда вы перейдете к Sonar, вы увидите, что эти классы покрыты 100%.

person ahmetcetin    schedule 16.06.2017
comment
добавление этого фактически снизило покрытие моего кода. Есть идеи, почему? - person Heisenberg; 18.08.2020
comment
Каково было освещение модельных классов и каково новое покрытие @Heisenberg - person ahmetcetin; 19.08.2020

В выпуске 0.8.0 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% :))

person mladzo    schedule 25.02.2018
comment
Это сработало, это фактически исключает геттеры и сеттеры ломбоков из тестового покрытия клевера. - person Rishabh Agarwal; 13.02.2019

Когда нужны равенства и хэш-код, их можно очень тщательно протестировать с помощью EqualsVerifier. EqualsVerifier — это библиотека JUnit с открытым исходным кодом, которая генерирует модульные тесты для всех частей контрактов equals и hashCode, чего непросто добиться даже при написании тестов вручную.

Пример использования:

@Test
public void equalsContract() {
    EqualsVerifier.forClass( MyAwesomeLombokedDataClass.class )
        .suppress( Warning.STRICT_INHERITANCE )
        .verify();
}
person Chris K    schedule 26.12.2017

Вы можете попробовать 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());
    }
}
person Anand Varkey Philips    schedule 07.06.2020
comment
Сумасшедшее количество аннотаций.. иногда я удивляюсь, во имя удобочитаемости они добавили сложности... вся цель побеждена ламбоком - person Stunner; 14.12.2020
comment
@Stunner, этот стек резюмируется в мета-аннотации @Data, что значительно упрощает работу. Он также сохраняет исходный код как можно более минимальным, а не сотнями геттеров и сеттеров на пути бизнес-логики. - person Stephan; 11.02.2021

создайте файл lombok.config на корневом уровне и добавьте этот код

config.stopBubbling = true
lombok.addLombokGeneratedAnnotation = true

будет игнорироваться покрытием кода

person Abdu E.    schedule 18.08.2020