Using `thenThrow()` and `thenReturn()` in the same test with Mockito allows you to simulate different behaviors for consecutive calls to a mocked method. This is particularly useful when testing scenarios where a method might fail initially but succeed on subsequent attempts. Here's how you can achieve this:
Understanding `thenThrow()` and `thenReturn()`
- `thenThrow()`: This method is used to specify that a mock should throw an exception when its method is called. It's commonly used to test error handling in your code.
- `thenReturn()`: This method is used to specify a return value for a mock method. You can chain multiple `thenReturn()` calls to define different return values for consecutive method calls.
Using `thenThrow()` and `thenReturn()` Together
To use both `thenThrow()` and `thenReturn()` in the same test, you need to chain them in a specific order. The first call to the mocked method will throw an exception, and subsequent calls will return the specified value(s).
Here is an example:
java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class MyServiceTest {
@Mock
private MyDependency myDependency;
@Test
void testConsecutiveCalls() {
// First call throws an exception, second call returns a value
when(myDependency.doSomething()).thenThrow(new RuntimeException("First call failed"))
.thenReturn("Success on second call");
// Simulate first call
try {
myDependency.doSomething();
} catch (RuntimeException e) {
System.out.println("Caught exception: " + e.getMessage());
}
// Simulate second call
String result = myDependency.doSomething();
System.out.println("Result on second call: " + result);
}
}
In this example, the first call to `myDependency.doSomething()` will throw a `RuntimeException`, and the second call will return the string `"Success on second call"`.
Best Practices
While it's technically possible to use both `thenThrow()` and `thenReturn()` in the same test, it's generally recommended to keep tests focused on a single scenario. This means you might want to consider writing separate tests for the exception case and the success case. However, if your test scenario genuinely requires simulating both behaviors in sequence, the approach above works well.
Using `thenAnswer()` for Custom Behavior
If you need more complex logic, such as changing behavior based on some condition or state, you might want to use `thenAnswer()` instead. This allows you to provide a custom implementation of the `Answer` interface to handle the method call.
java
when(myDependency.doSomething()).thenAnswer(invocation -> {
// Custom logic here, e.g., throw an exception on first call
static boolean firstCall = true;
if (firstCall) {
firstCall = false;
throw new RuntimeException("First call failed");
} else {
return "Success on subsequent calls";
}
});
This approach gives you more flexibility but can make your tests more complex.
Citations:[1] https://stackoverflow.com/questions/45597061/java-mock-throw-an-exception-then-return-a-value
[2] https://www.vogella.com/tutorials/Mockito/article.html
[3] https://dev.to/realnamehidden1_61/thenthrow-method-in-mockito-example-460l
[4] https://reflectoring.io/clean-unit-tests-with-mockito/
[5] https://www.toptal.com/java/a-guide-to-everyday-mockito
[6] https://stackoverflow.com/questions/4180209/mockito-re-stub-method-already-stubbed-with-thenthrow
[7] https://www.baeldung.com/mockito-exceptions
[8] https://www.lambdatest.com/automation-testing-advisor/selenium/methods/org.mockito.Mockito.doReturn