Mockito: Understanding how when().thenThrow() function works

Viewed 14226
when(mockObj.method(param1, param2)).thenReturn(1);
when(mockObj.method(param1, param2)).thenReturn(2);

When there are conflicting statements to return the value from a method with same argument list in a mocked object, I observed that the recent when/thenReturn will be returned. So, the below statement will be true.

assertEquals(2, mockObj.method(param1, param2));

When there are conflicting statements to throw exceptions, the behavior is not the same as above. For example,

@Test(expected = ExceptionTwo.class)
public void testMethod() {
    when(mockObj.method(param1, param2)).thenThrow(ExceptionOne.class);
    when(mockObj.method(param1, param2)).thenThrow(ExceptionTwo.class);
    mockObj.method(param1, param2);
}

This test case failed. Any explanation would be helpful.

2 Answers

In the first case, as the document referred here:

Warning : if instead of chaining .thenReturn() calls, 
multiple stubbing with the same matchers or arguments is used, 
then each stubbing will override the previous one.

So the second will override the first one. Hence, return 2. In other case, you can refer here:

when(mock.foo()).thenThrow(new RuntimeException());

//Impossible: the exception-stubbed foo() method is called so RuntimeException is thrown.
when(mock.foo()).thenReturn("bar");

//You have to use doReturn() for stubbing:
doReturn("bar").when(mock).foo();

When you use when..thenReturn, the stubbed method will be called. The first time you didn't observe it because it is called on the mocked object. But when you write when for the second time, then we have some behavior for mock.foo() (you set it previously to throw Exception). Hence, the second when(..) statement throws the exception in your case. So you should use doThrow().when(..).method(...) instead.

The first example is as per design, you can refer to the below link for more details.

https://static.javadoc.io/org.mockito/mockito-core/2.11.0/org/mockito/Mockito.html#stubbing_consecutive_calls

In the second example, the exception was raised while trying to register for stub one more time when(mockObj.method(param1,param2)).thenThrow(ExceptionTwo.class);. Kindly note that it's not due to statement mockObj.method(param1, param2);.

In my opinion, there might be actual method call while second when.thenThrow statement is executed. If you want to have different exceptions for subsequent calls, you can merge into one statement when(mockObj.method(param1, param2)).thenThrow(ExceptionOne.class, ExceptionTwo.class);

Related