Mockito Stubbing Consecutive Calls

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

  1. Creating a Mock:

    StockService stockService = mock(StockService.class);
    

    This line creates a mock object of the StockService class.

  2. Stubbing Consecutive Calls:

    when(stockService.getStock()).thenReturn(10).thenReturn(5).thenReturn(0);
    

    This line defines the behavior of the getStock method. The first call returns 10, the second call returns 5, and the third call returns 0.

  3. 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.

  4. Asserting the Results:

    assertEquals(10, stockLevel1);
    assertEquals(5, stockLevel2);
    assertEquals(0, stockLevel3);
    

    These lines check if the results are as expected.

  5. Verifying Interactions:

    verify(stockService, times(3)).getStock();
    

    This line verifies that the getStock method was called three times.

  6. 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 an IllegalArgumentException.

  7. 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.

  8. Asserting the Exception Message:

    assertEquals("Insufficient stock", exception.getMessage());
    

    This line checks if the exception message is as expected.

  9. 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.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top