MockitoException - is a *void method* and it *cannot* be stubbed with a *return value*!

时间:2016-02-12 21:29:30

标签: java junit mockito

I'm using Mockito for unit testingand I'm getting the following exception.

<%= form_for(@challenge, html: { data: { modal: true } })  do |f| %>
  <%= hidden_field_tag :step, 2 %>
  etc...
<% end %>

Here is the actual class

org.mockito.exceptions.base.MockitoException: 
`'setResponseTimeStampUtc'` 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.

this is the Test class I have written.

@Repository
public class CardRepositoryImpl implements ICardRepository {        
    @Autowired
    private OperationBean operation;

    @Override
    public OperationBean getCardOperation(final String cardHolderId, final String requestTimeStamp) {
        operation.setRequestTimeStampUtc(requestTimeStamp);
        operation.setResponseTimeStampUtc(DateUtil.getUTCDate());
        return operation;
    }
}

Is this an issue with the return statement since format() method is a final method.

@RunWith(MockitoJUnitRunner.class)
public class CardRepositoryImplUnitTestFixture_Mockito {
    @InjectMocks
    private CardRepositoryImpl cardRepositoryImpl;
    private OperationBean operationBean;

    @Before
    public void beforeTest() {
        MockitoAnnotations.initMocks(this);
    }
    @Test
    public void canGetCardOperation(){
        when(cardRepositoryImpl.getCardOperation("2", Mockito.anyString())).thenReturn(operationBean);

    }    
}

How can I resolve this?

1 个答案:

答案 0 :(得分:4)

@InjectMocks
private CardRepositoryImpl cardRepositoryImpl;

when(cardRepositoryImpl.getCardOperation("2", Mockito.anyString()))
    .thenReturn(operationBean);

cardRepositoryImpl不是模拟,因此您无法将其存根。使用@InjectMocks,您指示Mockito构造一个真正的CardRepositoryImpl,并将其私有字段和构造函数参数替换为测试用例中的字段。你可能想要使用间谍;更多关于这一点。

但首先,为什么会显示错误消息?因为它不是模拟,所以对cardRepositoryImpl.getCardOperation的调用发生在实际的CardRepositoryImpl实例上。 Mockito看到与operationBean的交互(这似乎是模拟),将whenthenReturn视为与最近的模拟通话相对应,并错误地告诉您&#39;使用返回值重写一个void方法(重要的是,错误的方法)。

在对CardRepositoryImpl的测试中,你不应该嘲笑CardRepositoryImpl,也不应该抄袭它来返回值:这不会测试任何东西,除了Mockito有效。你可能应该重新考虑存根需要什么才能让你的测试工作。

但是,当存根一个方法时,您可能希望在同一个类中存根另一个方法。这可以通过间谍来实现:

@Test
public void canGetCardOperation(){
    CardRepositoryImpl spyImpl = spy(cardRepositoryImpl);
    when(spyImpl.getCardOperation(Mockito.eq("2"), Mockito.anyString()))
        .thenReturn(returnValue);
    // ...
    spyImpl.someOtherMethod();  // Any matching calls to getCardOperation from
                                // someOtherMethod will use the stub above.
}

附注:在存根时,您会相邻地使用anyString"2"。当使用像anyString这样的匹配器时,如果您将它们用于任何参数,则需要将它们用于所有参数。 See more here.