Core Java

Mock Java Constructors using Mockito

Mockito‘s MockedConstruction feature allows developers to mock the construction of objects during testing, providing greater control and flexibility. This tutorial will explore leveraging Mockito’s MockedConstruction effectively with code examples to demonstrate its practical applications.

1. Understanding Mockito’s MockedConstruction

Mockito’s MockedConstruction feature empowers developers to intercept and modify the instantiation of objects within their test cases. Traditionally, when a new object is created within a method under test, it is challenging to mock or alter its behavior. MockedConstruction resolves this limitation by intercepting the object’s construction process, allowing developers to specify custom behaviors or return values for constructors.

Understanding the Fundamentals:

  • MockedConstruction(Class<T>): Creates a mock for constructions of class T, managing mocks within its scope.
  • try-with-resources Block: Encapsulates the mocking scope, ensuring automatic resource cleanup at block end.
  • whenever(mock.method(...)).thenReturn(...): Configures mock behavior, defining method responses dynamically.
  • assertEquals(mock, times(n)).method(...): Asserts interaction frequency with mocks.

2. Configuration

Key Consideration:

  • Mockito Version: Ensure you are using Mockito 3.5 or later, as earlier versions lack MockedConstruction.

2.1 Using Mockito 4

If using Mockito 4, we can utilize construction mocking with the inline mock maker activated in our project. To enable it, incorporate the mockito-core artifact and ensure the inclusion of the mockito-inline artifact in the project.

An illustration from a Maven pom.xml is provided below:

mockito-core maven dependency

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>4.2.0</version>
            <scope>test</scope>
        </dependency>

mockito-inline mock marker dependency

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>4.2.0</version>
            <scope>test</scope>
        </dependency>

2.1.1 Alternate Option

We can also configure the InMockMaker with an extension file by adding a org.mockito.plugins.MockMaker file inside src/test/resources/mockito-extensions with the following content:

mock-maker-inline

2.2 Using Mockito 5

With Mockito 5, the mockmaker-inline is enabled by default. We no longer need to include the mockito-inline artifact or create a org.mockito.plugins.MockMaker file to enable the mockmaker-inline. The remainder of this blog post will illustrate how to mock a constructor inside a @test annotated method using Mockito 5.

2.2.1 Maven Dependencies


<dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>5.3.1</version>
            <scope>test</scope>
        </dependency>
</dependencies>

3. Mocking Constructors

Mocking a constructor occurs within the try-with-resources block. Within this block, a mock is generated each time the constructor is invoked. However, outside of the try-with-resources block, the constructor remains unlocked.

3.1 Mocking a Simple Constructor

Let’s begin with a simple class named Calculator.java that we want to test:

public class Calculator {
    
    public Calculator(){}
    
     public int add(int a, int b) {
        return a + b;
    }
     
     public int subtract(int a, int b) {
        return a - b;
    }
}

To test the Calculator class, we will create a JUnit test class named CalculatorTest and mock the Calculator constructor using Mockito’s MockedConstruction:

public class CalculatorTest {

    @Test
    public void testAddition() {
        
        try (MockedConstruction<Calculator> mocked = mockConstruction(Calculator.class)) {

            // creating a mock instance
            Calculator calculator = new Calculator();

            // Mock behaviour
            when(calculator.add(10, 20)).thenReturn(30);
            when(calculator.subtract(30, 20)).thenReturn(10);

            // Test the methods
            assertEquals(30, calculator.add(10, 20));
            assertEquals(10, calculator.subtract(30, 20));

            // Get a list of all created mocks
            List<Calculator> constructed = mocked.constructed();
            assertEquals(1, constructed.size());

        }
    }
}

First, we define a test method named testAddition() annotated with @Test. Inside the try-with-resources statement, we mock the constructor of the Calculator class. Inside the block, we create a new instance of Calculator and set up mock behaviors for the add() and subtract() methods of the Calculator class using the when().thenReturn() syntax. For example, when calculator.add(10, 20) is called, it will return 30, and when calculator.subtract(30, 20) is called, it will return 10.

Next, we test the add() and subtract() methods of the Calculator class by calling them with specific arguments and asserting that the returned values match the expected results and finally retrieving a list of all created mocks using mocked.constructed(), asserting that only one mock was created. The test output using Netbeans IDE is:

Fig 1: output from a simple test class using java mockito mockedconstruction
Fig 1: output from a simple test class using java mockito mockedconstruction

3.2 Mocking Constructors Inside another Class

Let’s say we have a class DataProcessor that internally instantiates a DataProvider object using its constructor. We want to test the DataProcessor class without actually creating a DataProvider object. Here’s how you can achieve this using Mockito’s MockedConstruction:

DataProvider class

public class DataProvider {
    
    public DataProvider(){}
    
    public String fetchData() {
        // Some data fetching logic
        return "Mocked data";
    }
}

DataProcessor class

public class DataProcessor {
    
    private DataProvider dataProvider;

    public DataProcessor() {
        this.dataProvider = new DataProvider();
    }

    public String process() {
        String data = dataProvider.fetchData();
        // Process the data
        return "Processed: " + data;
    }
}

Now, let’s create a test for the DataProcessor class where we will mock the constructor of DataProvider:

public class DataProcessorTest {
    
    @Test
    public void testProcess() {
        
        try (MockedConstruction<DataProvider> mocked = mockConstruction(DataProvider.class)) {
            
            DataProcessor dataProcessor = new DataProcessor();
            
            DataProvider mockedProvider = mocked.constructed().get(0);
            
            when(mockedProvider.fetchData()).thenReturn("Mocked data");     

            assertEquals("Processed: Mocked data", dataProcessor.process());
        }
    }
    
}

In this example, we use mockConstruction(DataProvider.class) to mock the constructor of the DataProvider class. Inside the try block, we get the first mocked instance of DataProvider using mocked.constructed().get(0) and specify the behavior of its methods using Mockito’s when() method.

Finally, we create an instance of DataProcessor in our test case, which internally uses the mocked DataProvider. When we call the process() method on DataProcessor, it will use the mocked DataProvider, and we can verify the behavior of the DataProcessor class without actually creating a DataProvider object.

4. Mocking Constructors with parameters

Suppose we have a class Person with a parameterized constructor:


public class Person {
    
    private String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
}

To test a class that instantiates Person objects, we will mock the constructor of Person with parameters using the same try-with-resource statement as in the previous examples:

public class PersonTest {

    @Test
    public void testPersonManager() {
        try (MockedConstruction<Person> mocked = mockConstruction(Person.class)) {

            // create a mock instance
            Person person = new Person("John");

            when(person.getHeight()).thenReturn("190");
            when(person.getName()).thenReturn("John");

            String name = person.getName();

            assertEquals("190", person.getHeight());
            //assertEquals(null, person.getName());
            assertEquals("John", name);

            // Get list of all created mocks
            List<Person> constructed = mocked.constructed();
            assertEquals(1, constructed.size());
        }
    }
}

5. Change Default Mock Behavior

Sometimes, we may need to change the default behavior of the mocked object. Let’s say we want to change the default behavior of the add() method in the Calculator class:

public class TestCalculatorWithDefaultBehaviour {

    @Test
    public void testAddition() {

        try (MockedConstruction<Calculator> mocked = mockConstruction(Calculator.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS))) {

            Calculator calculator = new Calculator();
            assertEquals(30, calculator.add(10, 20));
            assertEquals(10, calculator.subtract(30, 20));
            
            System.out.println("Addition is :" + calculator.add(10, 20) + " Subtraction is: " + calculator.subtract(30, 20));

        }
    }
}

In this scenario, the MockedConstruction is once more encapsulated within a try-with-resources block. However, we introduce an additional parameter, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS) when instantiating the MockedConstruction. By employing this approach, we instruct it to generate a mock resembling a real Calculator instance for methods that we didn’t stub.

5. Conclusion

In this article, we have explored a few instances demonstrating the use of Mockito for mocking constructor calls. In conclusion, Mockito MockedConstruction empowers us to mock object construction in our test cases, offering greater control and flexibility. By selectively intercepting constructors, we can simulate complex scenarios and improve the testability of our codebases.

6. Download the Source Code

This was an example of using Java mockito mockedconstruction.

Download
You can download the full source code of this example here: Java mockito mockedconstruction

Omozegie Aziegbe

Omos holds a Master degree in Information Engineering with Network Management from the Robert Gordon University, Aberdeen. Omos is currently a freelance web/application developer who is currently focused on developing Java enterprise applications with the Jakarta EE framework.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button