2

When stubbing ClassOne.methodOne, I'm getting the following error message about stubbing a void method with a return value, even though ClassOne.methodOne is not void. The error seems to pertain to ClassTwo.methodTwo, even though I'm stubbing ClassOne.methodOne.

org.mockito.exceptions.base.MockitoException: 
`'methodTwo'` is a *void method* and it *cannot* be stubbed with a *return value*!
Voids are usually stubbed with Throwables:
    doThrow(exception).when(mock).someVoidMethod();
***

If you're unsure why you're getting above error read on.
Due to the nature of the syntax above problem might occur because:
1. The method you are trying to stub is *overloaded*. Make sure you are calling
   the right overloaded version.
2. Somewhere in your test you are stubbing *final methods*. Sorry, Mockito does not
   verify/stub final methods.
3. A spy is stubbed using `when(spy.foo()).then()` syntax. It is safer to stub
   spies with `doReturn|Throw()` family of methods. More in javadocs for
   Mockito.spy() method.

My code:

public class ClassOne {
  private ClassTwo classTwo;

  public boolean methodOne() {
    classTwo.methodTwo();
    return true;
  }
}

My test:

when(classOne.methodOne("some string")).thenReturn(true);

Why is this happening, and how do I fix it?

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251

1 Answers1

6

This can happen when you try to stub a method that Mockito cannot override. The way the syntax for when works, Mockito identifies the most recent call it can detect as the method call to stub. If Mockito cannot detect the method call you're stubbing, but can detect a call on a mock that happens within the actual implementation of the stubbed method, then it may mistake that call as the stubbing call. Thus, it thinks you are stubbing void method ClassTwo.methodTwo with a return value, and throws the exception.

/* 1 */  when(classOne.methodOne("some string")).thenReturn(true);
/* 2 */  when(              false              ).thenReturn(true);
/* 3 */  (    internal mockito stub object     ).thenReturn(true);

Normally, Mockito calls classOne.methodOne, which is an unstubbed method on a mock. Mockito's default implementation detects the call, records the invocation as the last-received invocation, and returns false. Then, in step 2 above, Mockito sees the call to when, marks that last invocation as a stub instead, and prepares for a call to thenVerb, which comes in step 3. However, in this case, during step one, Mockito hasn't overridden methodOne, so it can't detect the call or record the invocation. It actually calls classOne.methodOne, and if there are any interactions with a mock, those get recorded instead as if they were in the test above the when call. Step 2 proceeds as before, except that Mockito marks the wrong call for the stub, so step 3 sees a call to thenReturn for the void method.

This can be even more troublesome if you use Matchers, because if there are the wrong number of Matchers on the internal matcher stack, then you may get an InvalidUseOfMatchersException, even if the stubbing call in the test appears to use matchers correctly.


This problem crops up when Mockito cannot override the method you're stubbing. You'll need to check that the following is all true:

  • The class should be non-final, and should not have non-public parents.
  • The method should be non-static and non-final.
  • The instance should be a Mockito mock. Note that if it is created with @InjectMocks, it is a real implementation and not a mock; read more here.
  • As mentioned in the exception, when stubbing a spy, use doReturn/doAnswer/doThrow syntax, because otherwise you will call the spy's actual method from within the call to when.
Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Why can't Mockito overide `methodOne`? Is the issue in the question caused by `@InjectMocks`? Looking at your checklist, `ClassOne` is non-final and has no parent (other than `Object`), and `methodOne` is non-static and non-final. So the only 2 items left which could explain the issue are `@InjectMocks` and stubbing a spy. The question doesn't seem to say how the mock was created. – aro_tech Feb 14 '16 at 08:37
  • @aro_tech: This question is a generalization of [another recent Mockito question](http://stackoverflow.com/q/35372493/1426891), with a goal of a canonical question/answer pair as a dupe target. With the exact code listing in the question, InjectMocks is the likely reason as it was in the original linked question, but the misleading error could come from any of the listed reasons in the answer. – Jeff Bowman Feb 14 '16 at 22:57
  • This helped me solve my problem with intermittent mocking failures. The class under test made a call to `SwingUtilities.invokeLater` whose runnable called another method on the mock that was failing. – Rangi Keen Nov 02 '18 at 17:59