Аннотация Lombok @NonNull с @Builder не отражается в тестовом покрытии

Я столкнулся с проблемой при тестировании аннотированного класса lombok. Описание :-

Когда мы аннотируем класс Java (POJO) с помощью @Builder и имеем определенную переменную экземпляра с ограничениями @NonNull, при написании тестового примера для проверки NullPointerException мы не можем создать экземпляр POJO с помощью построителя и ожидать, что он будет оценивать против нулевой проверки. Я разобрал класс и увидел, что ненулевое ограничение течет внутри самого сгенерированного класса построителя, что означает, что исключение нулевого указателя генерируется еще до того, как мы создадим объект. В некотором смысле это правильное поведение, но мне нужно было знаю, могу ли я проверить такой сценарий.

@Builder
public class Sample {
    @NonNull
    private final String a;
    private final String b;
}

Теперь мне нужно проверить случай, когда NullPointerException генерируется в случае, если «a» равно Null. Для такого сценария у меня есть 2 варианта:

  • Либо я могу создать класс Sample с конструктором - new Sample(null, null)
  • Или могу создать через билдер - Sample.builder.a(null).build();

Мой вопрос касается второй части, так как первая будет работать нормально. Когда я сделаю это, я получу что-то вроде: -

public class Sample {
    @NonNull
    private final String a;
    private final String b;

    Sample(@NonNull String a, String b) {
        this.a = a;
        this.b = b;
    }

    public static SampleBuilder builder() {
        return new SampleBuilder();
    }

    public static class SampleBuilder {
        private @NonNull String a;
        private String b;

        SampleBuilder() {
        }

        public SampleBuilder a(@NonNull String a) {
            this.a = a;
            return this;
        }

        public SampleBuilder b(String b) {
            this.b = b;
            return this;
        }

        public Sample build() {
            return new Sample(a, b);
        }

        public String toString() {
            return "Sample.SampleBuilder(a=" + this.a + ", b=" + this.b + ")";
        }
    }
}

Здесь, если вы видите, что внутри самого SampleBuilder будет выброшено исключение NullPointerException, поскольку оно принимает @NonNull аргументов, а конструктор никогда не выполнит условие для проверки ненулевого атрибута, из-за которого упадет тестовое покрытие. Если мы используем @SuperBuilder , этого не произойдет, поскольку это не так. возьмите @NonNull в аргументах строителя.


person Radium Sharma    schedule 27.07.2020    source источник


Ответы (2)


Не совсем уверен, чего вы ожидаете, но приведенный ниже исходный код может правильно протестировать проверку null как конструктора, так и построителя класса Sample.

Вы можете увидеть его работающим по адресу https://repl.it/repls/CleverWiryTruespace.

import lombok.NonNull;
import lombok.Builder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Before;
import org.junit.rules.ExpectedException;
import org.junit.runner.JUnitCore;

class Main {
  @Builder
  public static class Sample {
      @NonNull
      private final String a;
      private final String b;
  }

  public static class TestSample {
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Before
    public void setUp() {
      thrown.expect(NullPointerException.class);
      thrown.expectMessage("a is marked non-null but is null");
    }

    @Test
    public void test1() {
      Sample s = new Sample(null, "abc");
    }

    @Test
    public void test2() {
      Sample.builder().a(null).build();
    }
  }

  public static void main(String[] args) {
    new JUnitCore().main(TestSample.class.getName());
  }
}
person David Rissato Cruz    schedule 28.07.2020
comment
Позвольте мне пояснить на примере вашего тестового примера. Чего я хотел добиться, так это получить NullPointerException на уровне конструктора, создав объект через построитель. - person Radium Sharma; 28.07.2020
comment
В приведенном выше примере (test2) будет выброшено исключение NullPointerException, но на уровне метода SampleBuilder, что означает, что я не могу покрыть сгенерированную ломбоком строку покрытия ветки для аргументов конструктора @NonNull, которая говорит что-то вроде if (a==null), выбросить NPE. Я могу добиться этого, вообще не устанавливая параметр 'a'. Я могу написать test2() следующим образом: Sample.builder().build(); Это не вызовет NPE на уровне конструктора, а на уровне конструктора. - person Radium Sharma; 28.07.2020
comment
Да, и это совершенно ожидаемо, так как это не касается шаблона строителя. Он не предназначен для предварительной проверки при вызове сборки. Это было бы лишним. Кроме того, шаблон построителя вызовет конструктор, который может даже иметь дополнительные ограничения. - person David Rissato Cruz; 29.07.2020
comment
Я думаю, вы пытаетесь получить 100% охват, но это неверно. Вы должны отфильтровать эти сгенерированные ломбоком методы из своего покрытия. 100% покрытие должно означать покрытие 100% ВАШЕГО КОДА, а не то, что было создано сторонней библиотекой. Вы должны доверять коду, предоставленному внешними зависимостями. - person David Rissato Cruz; 29.07.2020

Из кода видно, что если мы хотим запустить NPE на уровне конструктора, нам нужно оставить параметр @NonNull пустым, а не устанавливать для него значение null, что вызовет NPE на уровне метода компоновщика. Если я создам образец объекта следующим образом, этого должно быть достаточно для моего случая: -

Sample sample = Sample.builder.b("b").build();

Здесь я явно не устанавливаю для параметра «a» значение NULL, если я хочу проверить NPE «a» на уровне конструктора.

person Radium Sharma    schedule 28.07.2020