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 classT
, 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:
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.
You can download the full source code of this example here: Java mockito mockedconstruction