简体   繁体   中英

Mockito NumberFormat mocking NullPointer in when() method

so I've got this piece of code that throws me a NullPointerException

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

The problem is I get an exception within the when() method:

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

I've also tried to mock concrete class giving me the same results.

NumberFormat formatterFake = mock(DecimalFormat.class); 

I am kind of new to Mockito and any help would be very apericiated. Thanks in advance.

What's Happening

OK, so you're doing everything right here in order to stub a method on a mock.

NumberFormat is an abstract class. This is normally fine, as Mockito can mock abstract classes same as normal classes. The problem has to do with the implementation of the NumberFormat#format(long) method.

Taking a look at the source code for the implementation I'm using Oracle jdk 1.7.0 update 2, you can see what this method does:

public final String format(long number) {
        return format(number, new StringBuffer(), DontCareFieldPosition.INSTANCE).toString();
    }

The format method you are mocking is actually calling ANOTHER format method: NumberFormat#format(long, StringBuffer, FieldPosition) which resides in the same abstract class. It is then returning the result of calling toString() on the result of that call's result. It is a FINAL method, which Mockito can not stub.

When you use the when-then syntax to stub a method, Mockito actually calls the method you're stubbing (if it has a final implementation). So when you write:

when(formatterFake.format(1))

You are actually invoking that first overload of the format method implemented in the abstract NumberFormat class.

The final implementation of the 1st overload of format calls the 2nd format method. That 2nd format is an abstract method in NumberFormat . So there is no implementation to call.

No problem, we're working with a mock. Mockito supplies a Default Stubbing for every unimplemented method in the mock. By default, for all methods that return value, mock returns null, an empty collection or appropriate primitive/primitive wrapper value (eg: 0, false, ... for int/Integer, boolean/Boolean, ...).

So when trying to stub the call to NumberFormat#format(long) , because it has an implementation, you end up calling the default stubbing of NumberFormat#format(long, StringBuffer, FieldPosition) which returns null , which is then .toString() -ed and there you have the cause your NPE.

Solutions

Usually you would mock an interface, rather than mocking a class directly, which would avoid the potential for this type of problem entirely, as there would be no final anythings. However, since there is no interface available here to mock, it is not an option.

You could mock the 3-arg overload of format directly: it should then allow you to call the one argument version of format as desired:

  @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));
  }

However, I'm not sure this is the best solution. Maybe someone else will weigh in.

EDIT:

After seeing Garrett Hall's answer I made it more explicit that when I was talking about implementation I meant a final implementation.

As he suggests, PowerMock would allow mocking the final format method directly, if you're willing to go that route.

You will need to use PowerMock to mock a final method.

I'm not sure why you need to mock a final method on the NumberFormat class, that is a test smell. Please post your test as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM