Search code examples
javamockitoprivatepowermockpowermockito

Power Mockito Throws "Unfinished stubbing" error when method is private, but not error when method is protected


I am trying to test the method 'reallyCoolMethod' gets called with the correct parameters. The problem is stubbing the 'getSillyString' method is causing errors. When the method 'getSillyString' is private, the test will fail on the second doReturn:

doReturn("superSilly").when(spy, "getSillyString", 5, false);

however when the method is protected, the test will pass. The error is:

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at org.powermock.api.mockito.internal.PowerMockitoCore.doAnswer(PowerMockitoCore.java:36)

E.g. thenReturn() may be missing.
Examples of correct stubbing:
    when(mock.isOk()).thenReturn(true);
    when(mock.isOk()).thenThrow(exception);
    doThrow(exception).when(mock).someVoidMethod();
Hints:
 1. missing thenReturn()
 2. you are trying to stub a final method, you naughty developer!

Here is the class under test:

import java.util.Random;

@Singleton
public class FooBar {

  @Inject
  public FooBar(Foo foo, Bar bar) {
    this.foo = foo;
    this.bar = bar;
  }

  @POST
  @Path("foos/")
  public fooAction createFoo() {

     word1 = getSillyString(4, true);
     word2 = getSillyString(5, false);

     int id = reallyCoolMethod(word1,word2);
  }

  private String getSillyString(int size, boolean allCaps){
  }
}

Here is the test:

import static org.mockito.Mockito.verify;

import org.junit.Test;
import org.mockito.Mockito;

import static org.powermock.api.mockito.PowerMockito.doReturn;
import static org.powermock.api.mockito.PowerMockito.spy;

import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
public class FooTest {

    @Test
    public void testCreateFoo() throws Exception {

      Foo foo = Mockito.mock(Foo.class);
      Bar bar = Mockito.mock(Bar.class);
      FooBar resource = new FooBar(foo, bar);
      FooBar spy = spy(resource);

      doReturn("reallySilly").when(spy, "getSillyString", 4, true);
      doReturn("superSilly").when(spy, "getSillyString", 5, false);

      doReturn(1).when(spy).reallyCoolMethod(Mockito.anyString(),Mockito.anyString());
      spy.createFoo();
      verify(spy).reallyCoolMethod(Mockito.eq("reallySilly"),Mockito.Eq(superSilly));
    }

}

Solution

  • The real answer here: you created hard-to-test code by putting an essential element of that class into a private method.

    In other words: if that thing is so essential to your production code, then the better answer is to put the underlying functionality in its own class. So, you create some interface:

    public interface SillyService {
      public String getSillyString();
    }
    

    And then you use dependency injection to provide "some kind" of implementation to the class that needs this service.

    The desire to mock a private method always is a consequence of a bad design decision. Thus the answer does not lie within Mockito or PowerMock, but by stepping back and improving that design.

    For starters, one can watch these videos to learn how to write testable code instead.

    And beyond that: please note that PowerMock impacts the things you can do with Mockito - as PowerMock comes with pretty much backlevel versions of Mockito. My personal recommendation: get rid of your need for PowerMock; and instead just use latest/greatest versions of Mockito.