JUnit Assume.assumeNotNull抛出nullpointer而不是跳过测试

时间:2018-12-06 11:54:03

标签: groovy junit

我在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时跳过测试吗?

1 个答案:

答案 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[]的变量。

解决方案

有两种方法可以解决此问题。

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值的单个元素数组。 / p>