为@Nonnull注释参数编写单元测试

时间:2017-08-15 12:13:25

标签: java unit-testing exception nullpointerexception non-nullable

我有一个像这样的方法:

 public void foo(@Nonnull String value) {...}

我想编写一个单元测试来确保foo()valuenull时抛出一个NPE,但我不能,因为编译器在静态时拒绝编译单元测试在IDE中启用了空指针流分析。

如何使此测试编译(在Eclipse中启用“启用基于注释的空分析”):

@Test(expected = NullPointerException.class)
public void test() {
     T inst = ...
     inst.foo(null);
}

注意:理论上,编译器的静态空指针应该阻止这样的情况。但是没有什么可以阻止某人编写另一个模块并关闭静态流分析并使用null调用该方法。

常见案例:没有流量分析的大杂乱旧项目。我首先注释一些实用程序模块。在这种情况下,我将进行现有或新的单元测试,检查代码对使用流分析 的所有模块的行为。

我的猜测是,我必须将这些测试移到未经检查的模块中,然后在我进行流量分析时移动它们。这将有效并且非常适合哲学,但这将是很多手工工作。

换句话说:我不能轻易编写一个“代码无法编译时成功”的测试(我必须将代码片段放入文件中,从单元测试中调用编译器,检查输出因为错误...不太漂亮)。那么,当调用者忽略@Nonnull时,我如何轻松地 测试代码失败?

5 个答案:

答案 0 :(得分:7)

在方法中隐藏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给我一个编译错误 - 所以&#34;空检查&#34;已启用)。

与通过注释给出的解决方案相比,上面有一个很好的副作用,适用于任何类型的参数类型(但可能需要Java8才能始终使类型推断正确)。 / p>

是的,测试通过(如上所述),并在评论Objects.requireNonNull()行时失败。

答案 1 :(得分:2)

为什么不使用普通的旧反射?

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
}

请注意,重构时可能会意外中断(重命名方法,更改方法参数的顺序,将方法移动到新类型)。

答案 2 :(得分:0)

这里按合同设计来图片。您不能为使用notNull参数注释的方法提供null值参数。

答案 3 :(得分:0)

您可以使用初始化的字段,然后在设置方法中设置为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

答案 4 :(得分:0)

使用来自 Jupiter 断言的 assertThrows 我能够测试这个:

public MethodName(@NonNull final param1 dao) {....

assertThrows(IllegalArgumentException.class, () -> new MethodName(null));