8

我有一个像这样的方法:

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

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

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

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

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

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

我的猜测是我必须将这些测试移动到一个未经检查的模块中,并在我展开流分析时移动它们。这会奏效并且非常符合哲学,但这将是大量的手工工作。

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

4

5 回答 5

8

隐藏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()行时失败。

于 2017-08-15T14:53:20.880 回答
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
}

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

于 2017-08-15T19:10:59.073 回答
0

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

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

assertThrows(IllegalArgumentException.class, () -> new MethodName(null));
于 2021-02-12T00:22:43.943 回答
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.

于 2017-08-16T08:26:57.427 回答
0

在这里,按合同进行设计。您不能为使用 notNull 参数注释的方法提供空值参数。

于 2017-08-15T13:04:51.023 回答