发生了什么
好的,所以你在这里做所有事情是为了在模拟上存根一个方法。
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();
}
您正在模拟的格式方法实际上是调用另一个格式方法:NumberFormat#format(long, StringBuffer, FieldPosition)
它驻留在同一个抽象类中。然后它返回调用该toString()
调用结果的结果。这是一个FINAL方法,Mockito 不能存根。
当您使用 when-then 语法来存根方法时,Mockito 实际上会调用您正在存根的方法(如果它具有最终实现)。所以当你写:
when(formatterFake.format(1))
您实际上是在调用format
抽象NumberFormat
类中实现的方法的第一个重载。
第一次重载的最终实现format
调用第二个format
方法。第二个format
是NumberFormat
. 所以没有实现调用。
没问题,我们正在使用模拟。Mockito 为 mock 中每个未实现的方法提供一个 Default Stubbing。 默认情况下,对于所有返回值的方法,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));
}
但是,我不确定这是最好的解决方案。也许其他人会参与进来。
编辑:
在看到 Garrett Hall 的回答后,我更明确地说,当我谈论实施时,我的意思是最终实施。
正如他所建议的,如果您愿意走这条路,PowerMock 将允许直接模拟最终格式方法。