Mockito NumberFormat 在 when() 方法中模拟 NullPointer

And*_*ndy 5 java junit mockito

所以我有这段代码引发 NullPointerException

NumberFormat formatterFake = mock(NumberFormat.class);  
when(formatterFake.format(1)).thenReturn("1");
Run Code Online (Sandbox Code Playgroud)

问题是我在 when() 方法中遇到异常:

java.lang.NullPointerException
    at java.text.NumberFormat.format(NumberFormat.java:297)
Run Code Online (Sandbox Code Playgroud)

我还尝试模拟具体类,得到相同的结果。

java.lang.NullPointerException
    at java.text.NumberFormat.format(NumberFormat.java:297)
Run Code Online (Sandbox Code Playgroud)

我对 Mockito 有点陌生,任何帮助都会非常困难。提前致谢。

Tom*_*sky 5

发生了什么

好的,所以您在这里所做的一切都是为了在模拟上存根方法。

NumberFormat是一个抽象类。这通常很好,因为 Mockito 可以像普通类一样模拟抽象类。问题与该方法的实现有关NumberFormat#format(long)

看一下我使用Oracle jdk 1.7.0 update 2的实现的源代码,你可以看到这个方法的作用:

public final String format(long number) {
        return format(number, new StringBuffer(), DontCareFieldPosition.INSTANCE).toString();
    }
Run Code Online (Sandbox Code Playgroud)

您正在嘲笑的格式方法实际上正在调用另一个格式方法:NumberFormat#format(long, StringBuffer, FieldPosition)它驻留在同一个抽象类中。然后它返回调用调用结果toString()的结果。这是一个FINAL方法,Mockito 无法对其进行存根。

当您使用“when-then”语法来存根方法时,Mockito 实际上会调用您要存根的方法(如果它有最终实现)。所以当你写:

when(formatterFake.format(1))
Run Code Online (Sandbox Code Playgroud)

format您实际上正在调用抽象类中实现的方法的第一个重载NumberFormat

第一个重载的最终实现format调用第二个format方法。第二个format . NumberFormat所以没有可调用的实现。

没问题,我们正在使用模拟。Mockito 为模拟中每个未实现的方法提供默认存根。 默认情况下,对于所有返回值的方法,mock 返回 null、空集合或适当的基元/基元包装器值(例如:0、false、... 对于 int/Integer、boolean/Boolean、...)。

因此,当尝试对 的调用进行存根时NumberFormat#format(long),因为它有一个实现,所以您最终会调用NumberFormat#format(long, StringBuffer, FieldPosition)返回的默认存根null,然后将其.toString()进行 -ed 并在那里您得到 NPE 的原因。

解决方案

通常,您会模拟一个接口,而不是直接模拟一个类,这将完全避免出现此类问题的可能性,因为不会有任何最终的东西。但是,由于这里没有可用于模拟的接口,因此它不是一个选项。

您可以直接模拟 format 的 3-arg 重载:然后它应该允许您根据需要调用 format 的一个参数版本:

  @Test
  public void test() {
    final NumberFormat mockFormatter = mock(NumberFormat.class);

    final StringBuffer buffer = new StringBuffer("1");
    when(mockFormatter.format(anyLong(), any(StringBuffer.class), any(FieldPosition.class))).thenReturn(buffer);

    System.out.println("Result: " + mockFormatter.format(1));
  }
Run Code Online (Sandbox Code Playgroud)

但是,我不确定这是最好的解决方案。也许其他人会权衡。

编辑:

看到 Garrett Hall 的回答后,我更明确地表示,当我谈论实现时,我指的是最终实现。

正如他所建议的,如果您愿意走这条路,PowerMock 将允许直接模拟最终的格式方法。