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

时间:2012-05-15 10:35:07

标签: java junit mockito

所以我有这段代码会抛出一个NullPointerException

NumberFormat formatterFake = mock(NumberFormat.class);  
when(formatterFake.format(1)).thenReturn("1");

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

java.lang.NullPointerException
    at java.text.NumberFormat.format(NumberFormat.java:297)

我也试图模仿具体课程给我相同的结果。

NumberFormat formatterFake = mock(DecimalFormat.class); 

我对Mockito来说是个新手,任何帮助都会非常谨慎。提前谢谢。

2 个答案:

答案 0 :(得分:4)

发生了什么

好的,所以你要在这里做所有事情,以便在模拟上存根方法。

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为模拟中的每个未实现方法提供默认存根。 By default, for all methods that return value, mock returns null, an empty collection or appropriate primitive/primitive wrapper value (e.g: 0, false, ... for int/Integer, boolean/Boolean, ...).

因此,当尝试将调用存根到NumberFormat#format(long)时,因为它有一个实现,所以最终调用NumberFormat#format(long, StringBuffer, FieldPosition)的默认存根,返回null,然后{{1}在那里你有原因你的NPE。

解决方案

通常你会模拟一个接口,而不是直接模拟一个类,这样就完全避免了这类问题的可能性,因为没有最终的任何事情。但是,由于此处没有可用于模拟的界面,因此不能选择。

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

.toString()

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

修改

在看到Garrett Hall的回答之后,我更明确地说,当我谈论实现时,我的意思是最终实现。

正如他所说,如果你愿意走这条路,PowerMock会允许直接嘲笑最终的格式方法。

答案 1 :(得分:2)

您需要使用PowerMock来模拟最终方法。

我不确定为什么你需要在NumberFormat类上模拟最终方法,这是一种测试气味。请发布您的测试。