Question

I have a Tool class with two static methods, doSomething(Object) and callDoSomething(). The names are intuitive in that callDoSomething delegates its call to doSomething(Object);

public class Tool
{
  public static void doSomething( Object o )
  {
  }
  public static void callDoSomething()
  {
    doSomething( new Object());
  }
}

I have a Test class for Tool and I'd like to verify if doSomething(Object) was called (I want to do Argument Matching too in the future)

@RunWith( PowerMockRunner.class )
@PrepareForTest( { Tool.class } )
public class ToolTest
{
  @Test
  public void toolTest()
  {
    PowerMockito.mockStatic( Tool.class );
    Tool.callDoSomething();// error!!
    //Tool.doSomething();// this works! it gets verified!
    PowerMockito.verifyStatic();
    Tool.doSomething( Mockito.argThat( new MyArgMatcher() ) );
  }

  class MyArgMatcher extends ArgumentMatcher<Object>
  {
    @Override
    public boolean matches( Object argument )
    {
      return true;
    }
  }
}

Verify picks up doSomething(Object) if it's called directly. I've commented this code out above. Verify does NOT pick up doSomething(Object) when using callDoSomething, (this is the code shown above). This is my error log when running the code above:

Wanted but not invoked tool.doSomething(null);

However, there were other interactions with this mock.
    at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl.performIntercept(MockitoMethodInvocationControl.java:260)
    at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl.invoke(MockitoMethodInvocationControl.java:192)
    at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:105)
    at org.powermock.core.MockGateway.methodCall(MockGateway.java:60)
    at Tool.doSomething(Tool.java)
    at ToolTest.toolTest(ToolTest.java:22) 
... [truncated]

I'd like to avoid making any changes to the Tool class. My question is, how can I verify doSomething(Object) was called from callDoSomething(), as well as perform some argument matching on doSomething's param

Was it helpful?

Solution

It sounds like you want to use a static spy (partial mock). The section of the PowerMock documentation that talks about mocking static has a note in the second bullet that could be easily missed:

(use PowerMockito.spy(class) to mock a specific method)

Note, in your example you're not actually mocking the behavior, just verifying the method is called. There's a subtle but important difference. If you don't want doSomething(Object) to be called you'd need to do something like this:

@Test
public void toolTest() {
    PowerMockito.spy(Tool.class); //This will call real methods by default.

    //This will suppress the method call.
    PowerMockito.doNothing().when(Tool.class);
    Tool.doSomething(Mockito.argThat( new MyArgMatcher() ));

    Tool.callDoSomething();

    //The rest isn't needed since you're already mocking the behavior
    //but you can still leave it in if you'd like.
    PowerMockito.verifyStatic();
    Tool.doSomething(Mockito.argThat( new MyArgMatcher() ));
}

If you still want the method to fire though, just remove the two lines for doNothing(). (I added a simple System.out.println("do something " + o); to my version of Tool.java as an additional verification of doNothing().)

OTHER TIPS

You can do your validation with this:

public class Tool{

  public static boolean isFromCallDoSomethingMethod= false;







  public static void doSomething(Object o){

  }








  public static void callDoSomething() {

      doSomething(new Object());

      isFromCallDoSomethingMethod= true;

  }








}

You can do the verification as:

    if(Tool.isFromCallDoSomethingMethod){

        //you called doSomething() from callDoSomething();

    }

REMEMBER

Don't forget to do the validation if you call the doSomething() from another way that is not from callDoSomething(), you can do this by ussing Tool.isFromCallDoSomethingMethod = false

Is this what you want?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top