Introduction
In this chapter, we will learn about stubbing consecutive calls using Mockito. Stubbing consecutive calls allows you to define different behaviors for the same method when it is called multiple times. This is useful for testing scenarios where the method under test should exhibit different behavior on subsequent calls.
What is Stubbing?
Stubbing is the process of specifying what mock objects should do when their methods are called. By defining the behavior of mock objects, you can control their interactions and ensure that your tests are reliable and predictable. Stubbing allows you to return specific values, throw exceptions, or execute custom logic when a method is called.
Stubbing Consecutive Calls
Stubbing consecutive calls allows you to specify a sequence of behaviors for the same method. This can be useful when you want to simulate different responses or side effects for subsequent calls to the same method.
Example: StockService Class and StockServiceTest Class
Class Under Test: StockService
public class StockService {
private int stock = 0;
public int getStock() {
return stock;
}
public void addStock(int quantity) {
stock += quantity;
}
public void reduceStock(int quantity) {
if (stock >= quantity) {
stock -= quantity;
} else {
throw new IllegalArgumentException("Insufficient stock");
}
}
}
Test Class: StockServiceTest
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class StockServiceTest {
@Test
void testStubbingConsecutiveCalls() {
// Create a mock object of StockService
StockService stockService = mock(StockService.class);
// Stub the getStock method with consecutive calls
when(stockService.getStock()).thenReturn(10).thenReturn(5).thenReturn(0);
// Use the mock object
int stockLevel1 = stockService.getStock();
int stockLevel2 = stockService.getStock();
int stockLevel3 = stockService.getStock();
// Assert the results
assertEquals(10, stockLevel1);
assertEquals(5, stockLevel2);
assertEquals(0, stockLevel3);
// Verify the interactions
verify(stockService, times(3)).getStock();
}
@Test
void testStubbingConsecutiveCalls_WithException() {
// Create a mock object of StockService
StockService stockService = mock(StockService.class);
// Stub the reduceStock method to throw an exception on the second call
doNothing().doThrow(new IllegalArgumentException("Insufficient stock"))
.when(stockService).reduceStock(anyInt());
// Call the reduceStock method
stockService.reduceStock(5);
// Verify the first call
verify(stockService).reduceStock(5);
// Call the reduceStock method again and handle the exception
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
stockService.reduceStock(5);
});
// Assert the exception message
assertEquals("Insufficient stock", exception.getMessage());
// Verify the second call
verify(stockService, times(2)).reduceStock(5);
}
}
Explanation
-
Creating a Mock:
StockService stockService = mock(StockService.class);
This line creates a mock object of the
StockService
class. -
Stubbing Consecutive Calls:
when(stockService.getStock()).thenReturn(10).thenReturn(5).thenReturn(0);
This line defines the behavior of the
getStock
method. The first call returns10
, the second call returns5
, and the third call returns0
. -
Using the Mock Object:
int stockLevel1 = stockService.getStock(); int stockLevel2 = stockService.getStock(); int stockLevel3 = stockService.getStock();
These lines call the
getStock
method on the mock object multiple times and store the results. -
Asserting the Results:
assertEquals(10, stockLevel1); assertEquals(5, stockLevel2); assertEquals(0, stockLevel3);
These lines check if the results are as expected.
-
Verifying Interactions:
verify(stockService, times(3)).getStock();
This line verifies that the
getStock
method was called three times. -
Stubbing Consecutive Calls with Exception:
doNothing().doThrow(new IllegalArgumentException("Insufficient stock")) .when(stockService).reduceStock(anyInt());
This line defines the behavior of the
reduceStock
method. The first call does nothing, and the second call throws anIllegalArgumentException
. -
Using the Mock Object with Exception:
stockService.reduceStock(5); Exception exception = assertThrows(IllegalArgumentException.class, () -> { stockService.reduceStock(5); });
These lines call the
reduceStock
method on the mock object multiple times and handle the exception thrown on the second call. -
Asserting the Exception Message:
assertEquals("Insufficient stock", exception.getMessage());
This line checks if the exception message is as expected.
-
Verifying Interactions with Exception:
verify(stockService).reduceStock(5); verify(stockService, times(2)).reduceStock(5);
These lines verify that the
reduceStock
method was called the expected number of times.
Conclusion
Stubbing consecutive calls using Mockito allows you to define different behaviors for the same method when it is called multiple times. This is useful for testing scenarios where the method under test should exhibit different behavior on subsequent calls. By using methods like thenReturn()
and doThrow()
, you can create flexible and dynamic tests that simulate a wide range of real-world conditions.