Я думал, что это может быть ошибкой, но я думаю, что это скорее следствие динамической системы типов Groovy в ее поведении по умолчанию.Assume.assumeNotNull(Object... objects)
метод использует параметры varargs.Это означает, что в случае передачи не-массивного элемента компилятор оборачивает его в массив ожидаемого типа.
String getenv = System.getenv("bogos");
Assume.assumeNotNull(getenv); // --> Assume.assumeNotNull(new Object[] { getenv });
Это то, что делает статический компилятор Java.Таким образом, в случае getenv == null
мы получаем:
Assume.assumeNotNull(new Object[] { null });
Непустой массив, который содержит один нулевой элемент.С другой стороны, если мы укажем переменную с типом массива и присвоим ей значение null
, вызов этого же метода вызовет NullPointerException
, как в следующем примере:
String[] array = null;
Assume.assumeNotNull(array); // --> throws NPE
Это, по крайней мере, то, что происходит в Java.Теперь, почему это не проходит в модульном тесте Groovy?Groovy по умолчанию является динамически типизированным языком, поэтому в этой области он ведет себя совершенно иначе.Похоже, что система типов Groovy применяет значение null
к типу метода, не заключая его в массив, поэтому в случае передачи null
методу, который ожидает, например, Object... objects
, мы всегда получаем objects == null
вместо objects == new Object[] { null }
.
Я начал задавать себе вопрос, это ошибка или нет.С одной стороны, я ожидаю, что динамический Groovy будет вести себя так же, как статически скомпилированный код.Но, с другой стороны, в динамически типизированной системе это различие допустимо (и, возможно, даже желательно), потому что динамическая система типов выводит тип во время выполнения.Он видит null
, поэтому думает, что мы намереваемся присвоить null
значение переменной типа Object[]
.
Решение
Существует два способа решения этой проблемы.
1.Включите статическую компиляцию
Если вы не используете динамические и метапрограммирующие функции Groovy в своем тестовом примере, вы можете легко аннотировать его аннотацией @groovy.transform.CompileStatic
, чтобы сгенерировать байт-код, который больше похож на байт-код Java.Например, вот как выглядит байт-код вашего метода в динамическом Groovy:
@Test
public void skipWhenNull() throws Exception {
CallSite[] var1 = $getCallSiteArray();
Object getenv = var1[4].call(System.class, "bogos");
if (ScriptBytecodeAdapter.compareEqual(getenv, (Object)null)) {
var1[5].callCurrent(this, "Its null!");
}
var1[6].call(Assume.class, getenv);
var1[7].callCurrent(this, "Test executing!");
}
А вот тот же метод, но помеченный @CompileStatic
с точки зрения байт-кода:
@Test
public void skipWhenNull() throws Exception {
String getenv = System.getenv("bogos");
Object var10000;
if (getenv == null) {
DefaultGroovyMethods.println(this, "Its null!");
var10000 = null;
}
Assume.assumeNotNull(new Object[]{getenv});
var10000 = null;
DefaultGroovyMethods.println(this, "Test executing!");
var10000 = null;
}
2.Оберните getenv
массивом
В качестве альтернативы, вы можете сделать более явный вызов метода Assume.assumeNotNull
.Если вы замените:
Assume.assumeNotNull(getenv);
на:
Assume.assumeNotNull([getenv] as Object[]);
, тогда вы явным образом перенесете параметр в массив Object[]
и не допустите передачу объекта массива, представленного null
, но вместо этого один массив элементов, содержащий null
значение.