Mockito Injecting Mocks

Introduction

In this chapter, we will learn about injecting mocks using Mockito. Injecting mocks can simplify the process of setting up dependencies for the class under test. This approach allows you to automatically inject mocked dependencies into the class, making the tests cleaner and more maintainable.

What is Injecting Mocks?

Injecting mocks involves using annotations to automatically create and inject mock objects into the class under test. Mockito provides the @Mock and @InjectMocks annotations to facilitate this process. The @Mock annotation creates a mock object, and the @InjectMocks annotation creates an instance of the class under test and injects the mocks into it.

Setting Up Mockito for Injecting Mocks

Ensure that you have the necessary dependencies in your pom.xml if you are using Maven:

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.2.0</version>
    <scope>test</scope>
</dependency>
<!-- JUnit 5 dependency -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.8.2</version>
    <scope>test</scope>
</dependency>

Example: CalculatorService Class and CalculatorServiceTest Class

Class Under Test: CalculatorService

public class CalculatorService {
    private final MathService mathService;

    public CalculatorService(MathService mathService) {
        this.mathService = mathService;
    }

    public int add(int a, int b) {
        return mathService.add(a, b);
    }

    public int subtract(int a, int b) {
        return mathService.subtract(a, b);
    }
}

Supporting Class: MathService

public class MathService {
    public int add(int a, int b) {
        return a + b;
    }

    public int subtract(int a, int b) {
        return a - b;
    }
}

Test Class: CalculatorServiceTest

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;

public class CalculatorServiceTest {

    @Mock
    private MathService mathService;

    @InjectMocks
    private CalculatorService calculatorService;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void testAdd_Success() {
        // Arrange
        when(mathService.add(10, 20)).thenReturn(30);

        // Act
        int result = calculatorService.add(10, 20);

        // Assert
        assertEquals(30, result);
        verify(mathService).add(10, 20);
    }

    @Test
    void testSubtract_Success() {
        // Arrange
        when(mathService.subtract(20, 10)).thenReturn(10);

        // Act
        int result = calculatorService.subtract(20, 10);

        // Assert
        assertEquals(10, result);
        verify(mathService).subtract(20, 10);
    }
}

Explanation

  1. Annotations:
    @Mock
    private MathService mathService;
    
    @InjectMocks
    private CalculatorService calculatorService;
    

    These annotations are used to create mock objects (@Mock) and inject them into the class under test (@InjectMocks).

  2. Injecting Mocks:
    When Mockito sees the @InjectMocks annotation, it creates an instance of the CalculatorService class and injects the MathService mock into it. This is how the dependencies are automatically injected.
  3. Initializing Mocks:
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
    

    This method initializes the mocks before each test method runs, ensuring that the CalculatorService instance has its dependencies injected.

  4. Test Method:
    @Test
    void testAdd_Success() {
        // Arrange
        when(mathService.add(10, 20)).thenReturn(30);
    
        // Act
        int result = calculatorService.add(10, 20);
    
        // Assert
        assertEquals(30, result);
        verify(mathService).add(10, 20);
    }
    

    This test method sets up the mock behavior (Arrange), calls the method under test (Act), and checks the expected results (Assert). It verifies that the dependencies are correctly injected and used.

Output

Mockito Injecting Mocks

Real-World Example: OrderService Class and OrderServiceTest Class

Class Under Test: OrderService

public class OrderService {
    private final PaymentService paymentService;
    private final InventoryService inventoryService;

    public OrderService(PaymentService paymentService, InventoryService inventoryService) {
        this.paymentService = paymentService;
        this.inventoryService = inventoryService;
    }

    public boolean placeOrder(String productId, int quantity) {
        if (inventoryService.checkStock(productId, quantity)) {
            paymentService.processPayment(productId, quantity);
            return true;
        }
        return false;
    }
}

Supporting Classes: PaymentService and InventoryService

public class PaymentService {
    public void processPayment(String productId, int quantity) {
        // Logic to process payment
    }
}

public class InventoryService {
    public boolean checkStock(String productId, int quantity) {
        // Logic to check stock
        return true;
    }
}

Test Class: OrderServiceTest

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;

public class OrderServiceTest {

    @Mock
    private PaymentService paymentService;

    @Mock
    private InventoryService inventoryService;

    @InjectMocks
    private OrderService orderService;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void testPlaceOrder_Success() {
        // Arrange
        when(inventoryService.checkStock("product123", 2)).thenReturn(true);

        // Act
        boolean result = orderService.placeOrder("product123", 2);

        // Assert
        assertTrue(result);
        verify(inventoryService).checkStock("product123", 2);
        verify(paymentService).processPayment("product123", 2);
    }

    @Test
    void testPlaceOrder_Failure() {
        // Arrange
        when(inventoryService.checkStock("product123", 2)).thenReturn(false);

        // Act
        boolean result = orderService.placeOrder("product123", 2);

        // Assert
        assertFalse(result);
        verify(inventoryService).checkStock("product123", 2);
        verify(paymentService, never()).processPayment(anyString(), anyInt());
    }
}

Explanation

  1. Annotations:
    @Mock
    private PaymentService paymentService;
    
    @Mock
    private InventoryService inventoryService;
    
    @InjectMocks
    private OrderService orderService;
    

    These annotations are used to create mock objects (@Mock) and inject them into the class under test (@InjectMocks).

  2. Injecting Mocks:
    When Mockito sees the @InjectMocks annotation, it creates an instance of the OrderService class and injects the PaymentService and InventoryService mocks into it. This is how the dependencies are automatically injected.
  3. Initializing Mocks:
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
    

    This method initializes the mocks before each test method runs, ensuring that the OrderService instance has its dependencies injected.

  4. Test Method:
    @Test
    void testPlaceOrder_Success() {
        // Arrange
        when(inventoryService.checkStock("product123", 2)).thenReturn(true);
    
        // Act
        boolean result = orderService.placeOrder("product123", 2);
    
        // Assert
        assertTrue(result);
        verify(inventoryService).checkStock("product123", 2);
        verify(paymentService).processPayment("product123", 2);
    }
    

    This test method sets up the mock behavior (Arrange), calls the method under test (Act), and checks the expected results (Assert). It verifies that the dependencies are correctly injected and used.

Conclusion

Injecting mocks using Mockito makes the setup of tests cleaner and more maintainable. By using the @Mock and @InjectMocks annotations, you can easily create and inject mock dependencies into the class under test. This approach simplifies the test code and ensures that the dependencies are properly mocked. In this chapter, we demonstrated how to use Mockito to inject mocks with a simple CalculatorService example and a real-world OrderService example. This method makes your tests more readable and maintainable, improving the overall quality of your test suite.

Leave a Comment

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

Scroll to Top