Написание модульного теста для аннотированного параметра @Nonnull
У меня есть такой метод:
public void foo(@Nonnull String value) {...}
Я хотел бы написать модульный тест, чтобы убедиться, foo()
бросает NPE, когда value
является null
но я не могу, так как компилятор отказывается компилировать модульный тест, когда статический анализ потока нулевого указателя включен в IDE.
Как мне сделать эту тестовую компиляцию (в Eclipse с включенным "Включить аннулирование на основе аннотаций"):
@Test(expected = NullPointerException.class)
public void test() {
T inst = ...
inst.foo(null);
}
Примечание: теоретически статический нулевой указатель компилятора должен предотвращать подобные случаи. Но ничто не мешает кому-то написать другой модуль с отключенным статическим анализом потока и вызвать метод с null
,
Типичный случай: старый грязный старый проект без анализа потока. Я начну с аннотирования некоторого служебного модуля. В этом случае у меня будут существующие или новые модульные тесты, которые проверяют поведение кода для всех модулей, которые еще не используют анализ потока.
Я предполагаю, что мне нужно перенести эти тесты в непроверенный модуль и перемещать их по мере распространения анализа потока. Это будет работать и хорошо вписываться в философию, но это будет много ручной работы.
Другими словами: я не могу легко написать тест, который говорит: "Успех, когда код не компилируется" (мне пришлось бы помещать куски кода в файлы, вызывать компилятор из модульных тестов, проверять вывод на наличие ошибок... не красиво). Итак, как я могу легко проверить, что код не работает, как это должно быть, когда вызывающие игнорируют @Nonnull
?
5 ответов
Прячется null
в методе делает трюк:
public void foo(@NonNull String bar) {
Objects.requireNonNull(bar);
}
/** Trick the Java flow analysis to allow passing <code>null</code>
* for @Nonnull parameters.
*/
@SuppressWarnings("null")
public static <T> T giveNull() {
return null;
}
@Test(expected = NullPointerException.class)
public void testFoo() {
foo(giveNull());
}
Выше скомпилируется нормально (и да, дважды проверил - при использовании foo(null)
моя IDE дает мне ошибку компиляции - так что "проверка нуля" включена).
В отличие от решения, предоставленного в комментариях, вышеприведенное имеет приятный побочный эффект для работы с любым типом параметра (но, вероятно, может потребоваться Java8, чтобы всегда делать правильный вывод типа).
И да, тест проходит (как написано выше) и не проходит при комментировании Objects.requireNonNull()
линия.
Почему бы просто не использовать простое старое отражение?
try {
YourClass.getMethod("foo", String.class).invoke(someInstance, null);
fail("Expected InvocationException with nested NPE");
} catch(InvocationException e) {
if (e.getCause() instanceof NullPointerException) {
return; // success
}
throw e; // let the test fail
}
Обратите внимание, что это может неожиданно прерваться при рефакторинге (вы переименуете метод, измените порядок параметров метода, переместите метод на новый тип).
Используя assertThrows из утверждений Юпитера, я смог проверить это:
public MethodName(@NonNull final param1 dao) {....
assertThrows(IllegalArgumentException.class, () -> new MethodName(null));
Здесь дизайн по контракту приходит к картине. Вы не можете предоставить нулевое значение параметра методу, аннотированному аргументом notNull.
Вы можете использовать поле, которое вы инициализируете, а затем установите null
в методе настройки:
private String nullValue = ""; // set to null in clearNullValue()
@Before
public void clearNullValue() {
nullValue = null;
}
@Test(expected = NullPointerException.class)
public void test() {
T inst = ...
inst.foo(nullValue);
}
Как и в ответе GhostCat, компилятор не может знать, когда и когда clearNullValue()
называется и должен предполагать, что поле не null
,