我在groovy中有这个junit测试(使用JUnit 4.12),只有在 getenv!= null :
时才应执行 import org.junit.Assume
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
...
@Test
public void skipWhenNull() throws Exception {
def getenv = System.getenv("bogos")
if( getenv == null) {
println "Its null!"
}
Assume.assumeNotNull(getenv);
println "Test executing!"
}
但是当我运行测试时,它会打印:Its null!
,然后引发NullPointer异常(在行:Assume.assumeNotNull(getenv);
中)。的意思是:Assume.assumeNotNull(expr)
不是要在expr
评估为null而不是抛出Nullpointer时跳过测试吗?
答案 0 :(得分:1)
我认为这可能是一个错误,但是我认为这是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元素的非空数组。另一方面,如果我们指定一个具有数组类型的变量,并为其分配一个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[]
的变量。
有两种方法可以解决此问题。
如果在测试用例中不使用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;
}
getenv
或者,您可以对Assume.assumeNotNull
方法进行更明确的调用。如果您替换:
Assume.assumeNotNull(getenv);
具有:
Assume.assumeNotNull([getenv] as Object[]);
然后,您将使用Object[]
数组显式包装参数,并且将防止传递由null
表示的数组对象,而是传递包含null
值的单个元素数组。 / p>