Mockito Mocking Final Classes and Methods

Introduction

In this chapter, we will learn about mocking final classes and methods using Mockito. Final classes and methods cannot be subclassed, which can make them difficult to mock in tests. However, Mockito provides functionality to mock these as well, allowing you to isolate your tests from the actual implementations and making them more robust and easier to maintain.

What is Mocking Final Classes and Methods?

Mocking final classes and methods involves creating a mock for classes and methods that are declared as final. This is useful for isolating tests from the actual implementations of final methods and classes, which cannot be overridden.

Setting Up Mockito to Mock Final Classes and Methods

To mock final classes and methods with Mockito, you need to use the mockito-inline library, which provides the necessary functionality. Ensure that you have the following dependencies in your pom.xml if you are using Maven:

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</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 1: Simple Final Class with Final Method

Let’s start with a simple final class that has a final method. We will mock this final method in a test.

Final Class with Final Method

public final class SimpleFinalClass {
    public final String greet(String name) {
        return "Hello, " + name;
    }
}

Test Class: SimpleFinalClassTest

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class SimpleFinalClassTest {

    @Test
    void testGreet_MockFinalMethod() {
        // Create a mock object of SimpleFinalClass
        SimpleFinalClass simpleFinalClass = mock(SimpleFinalClass.class);

        // Mock the final method
        when(simpleFinalClass.greet(anyString())).thenReturn("Hi there!");

        // Call the method under test
        String result = simpleFinalClass.greet("John");

        // Assert the result
        assertEquals("Hi there!", result);

        // Verify that the final method was called
        verify(simpleFinalClass).greet("John");
    }
}

Explanation

  1. Creating Mocks:
    SimpleFinalClass simpleFinalClass = mock(SimpleFinalClass.class);
    

    This line creates a mock object of the SimpleFinalClass final class.

  2. Mocking Final Method:
    when(simpleFinalClass.greet(anyString())).thenReturn("Hi there!");
    

    This line mocks the greet final method to return "Hi there!" regardless of the input argument.

  3. Calling the Method Under Test:
    String result = simpleFinalClass.greet("John");
    

    This line calls the greet method on the SimpleFinalClass instance with the argument "John".

  4. Asserting the Result:
    assertEquals("Hi there!", result);
    

    This line asserts that the returned value matches the expected result "Hi there!".

  5. Verifying the Final Method Was Called:
    verify(simpleFinalClass).greet("John");
    

    This line verifies that the greet final method was called with the argument "John".

Example 2: Utility Final Class with Final Methods

Now let’s consider a more practical example where we have a final class MathUtils with final methods add and multiply. We will mock these methods to test a CalculatorService class that uses these methods.

Final Class with Final Methods

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

    public final int multiply(int a, int b) {
        return a * b;
    }
}

Service Class Using Final Methods

public class CalculatorService {
    private final MathUtils mathUtils;

    public CalculatorService(MathUtils mathUtils) {
        this.mathUtils = mathUtils;
    }

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

    public int calculateProduct(int a, int b) {
        return mathUtils.multiply(a, b);
    }
}

Test Class: CalculatorServiceTest

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class CalculatorServiceTest {

    @Test
    void testCalculateSum_MockFinalMethod() {
        // Create a mock object of MathUtils
        MathUtils mathUtils = mock(MathUtils.class);

        // Mock the final method
        when(mathUtils.add(anyInt(), anyInt())).thenReturn(10);

        // Create an instance of CalculatorService
        CalculatorService calculatorService = new CalculatorService(mathUtils);

        // Call the method under test
        int result = calculatorService.calculateSum(3, 7);

        // Assert the result
        assertEquals(10, result);

        // Verify that the final method was called
        verify(mathUtils).add(3, 7);
    }

    @Test
    void testCalculateProduct_MockFinalMethod() {
        // Create a mock object of MathUtils
        MathUtils mathUtils = mock(MathUtils.class);

        // Mock the final method
        when(mathUtils.multiply(anyInt(), anyInt())).thenReturn(20);

        // Create an instance of CalculatorService
        CalculatorService calculatorService = new CalculatorService(mathUtils);

        // Call the method under test
        int result = calculatorService.calculateProduct(4, 5);

        // Assert the result
        assertEquals(20, result);

        // Verify that the final method was called
        verify(mathUtils).multiply(4, 5);
    }
}

Explanation

  1. Creating Mocks:
    MathUtils mathUtils = mock(MathUtils.class);
    

    This line creates a mock object of the MathUtils final class.

  2. Mocking Final Methods:
    when(mathUtils.add(anyInt(), anyInt())).thenReturn(10);
    when(mathUtils.multiply(anyInt(), anyInt())).thenReturn(20);
    

    These lines mock the add and multiply final methods to return 10 and 20 respectively, regardless of the input arguments.

  3. Creating an Instance of CalculatorService:
    CalculatorService calculatorService = new CalculatorService(mathUtils);
    

    This line creates an instance of the CalculatorService class with the mock dependency.

  4. Calling the Methods Under Test:
    int resultSum = calculatorService.calculateSum(3, 7);
    int resultProduct = calculatorService.calculateProduct(4, 5);
    

    These lines call the calculateSum and calculateProduct methods on the CalculatorService instance with the specified arguments.

  5. Asserting the Results:
    assertEquals(10, resultSum);
    assertEquals(20, resultProduct);
    

    These lines assert that the returned values match the expected results.

  6. Verifying the Final Methods Were Called:
    verify(mathUtils).add(3, 7);
    verify(mathUtils).multiply(4, 5);
    

    These lines verify that the add and multiply final methods were called with the specified arguments.

Output

Mockito Mocking Final Classes and Methods

Conclusion

Mocking final classes and methods using Mockito allows you to isolate your tests from the actual implementations of final methods and classes. By using the mockito-inline library, you can mock final methods and verify their interactions, making your tests more robust and maintainable. Understanding and utilizing final method mocking will help you write more effective and isolated unit tests. In this chapter, we covered two examples: a simple final class and a utility final class with methods used in a service class. These examples demonstrate how to use Mockito to mock final methods and verify their behavior in different scenarios.

Leave a Comment

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

Scroll to Top