Starting with Mockito version 3.5.0, we can now mock Java constructors with Mockito. This allows us to return a mock from every object construction for testing purposes. Similar to mocking static method calls with Mockito, we can define the scope of when to return a mock from a Java constructor for a particular Java class. While the practical (and reasonable) use cases for this feature might be limited, at least it's the next step towards replacing PowerMock with Mockito entirely. Let's take a look at this feature in action.
Configuration of the Mockito InlineMockMaker
Update: Starting with Mockito version 5.0.0, the mockito-inline
is now the default MockMaker
. If we're using this version, we can skip the configuration effort below.
This new feature of Mockito is only available if we use an InlineMockMaker
. The InlineMockMaker
is an interface that extends the MockMaker
interface that defines Mockito's internal API to create mocks. There are multiple implementations for this interface, and the default one is the SubclassByteBuddyMockMaker
.
To override the default MockMaker
and use a InlineMockMaker
instead, we can replace our mockito-core
dependency with mockito-inline
:
1 2 3 4 5 6 7 | <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <!-- version needs to be >= 3.5.0 --> <version>${mockito.version}</version> <scope>test</scope> </dependency> |
If, for some reason, we are not able to add this dependency to our project and only rely on mockito-core
, we can still configure the InMockMaker
with an extension file.
Therefore, we can put a org.mockito.plugins.MockMaker
file inside src/test/resources/mockito-extensions
with the following content:
1 | mock-maker-inline |
For those using the Spring Boot Starter Test to manage the versions of the basic testing libraries, refer to the Mockito setup section of this article.
Mocking a Java Constructor With Mockito
Let's see how we can mock the construction of an object with Mockito. As an example, we'll use the following Java class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public class PaymentProcessor { private boolean allowForeignCurrencies; private String fallbackOption; private BigDecimal taxRate; public PaymentProcessor() { this(false, "DEBIT", new BigDecimal("19.00")); } public PaymentProcessor(String fallbackOption, BigDecimal taxRate) { this(false, fallbackOption, taxRate); } public PaymentProcessor(boolean allowForeignCurrencies, String fallbackOption, BigDecimal taxRate) { this.allowForeignCurrencies = allowForeignCurrencies; this.fallbackOption = fallbackOption; this.taxRate = taxRate; } public BigDecimal chargeCustomer(String customerId, BigDecimal netPrice) { // any arbitrary implementation System.out.println("About to charge customer: " + customerId); return BigDecimal.ZERO; } } |
Furthermore, let's assume an instance of this PaymentProcess
is created manually inside the CheckoutService
using the new
operator.
1 2 3 4 5 6 7 8 9 | public class CheckoutService { public BigDecimal purchaseProduct(String productName, String customerId) { // any arbitrary implementation PaymentProcessor paymentProcessor = new PaymentProcessor(); return paymentProcessor.chargeCustomer(customerId, BigDecimal.TEN); } } |
If we used constructor/field/setter injection instead, this Mockito feature wouldn't be relevant. In such cases, we can pass the mocked version of a collaborator and don't have to mock the constructor.
When writing a unit test for the purchaseProduct
method, we want to mock collaborators of our class under test. For this purpose, we can now use the new feature of Mockito and mock the object construction of the PaymentProcessor
inside purchaseProduct
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class CheckoutServiceTest { @Test void mockObjectConstruction() { try (MockedConstruction<PaymentProcessor> mocked = Mockito.mockConstruction(PaymentProcessor.class, (mock, context) -> { // further stubbings ... when(mock.chargeCustomer(anyString(), any(BigDecimal.class))).thenReturn(BigDecimal.TEN); })) { CheckoutService cut = new CheckoutService(); BigDecimal result = cut.purchaseProduct("MacBook Pro", "42"); assertEquals(BigDecimal.TEN, result); } } } |
The new method that makes mocking object constructions possible is Mockito.mockConstruction()
. This method takes a non-abstract Java class that constructions we're about to mock as a first argument. In the example above, we use an overloaded version of mockConstruction()
to pass a MockInitializer
as a second argument. This MockInitializer
is a callback that we use to stub the behavior of the mock.
We can define the scope of mocking any object creation for our PaymentProcessor
by using Java's try-with-resources construct, as the MockedConstruction
is extending the AutoClosable
interface. This allows mocking the object construction only for a temporary and user-defined purpose.
If you've already mocked a static method with Mockito, this setup might seem familiar.
Let's take a look at further examples of how we can use this new feature of Mockito.
Further Examples to Mock Java Constructors with Mockito
First, let's come back to the scope of mocking the object construction. Whenever we use the try-with-resources
(highly recommended for this use case) construct, every constructor call inside this block is mocked. Any object creation before or after returns a real instance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class PaymentProcessorTest { @Test void mockObjectConstruction() { // a real object of the PaymentProcessor is returned System.out.println(new PaymentProcessor().chargeCustomer("42", BigDecimal.valueOf(42))); try (MockedConstruction<PaymentProcessor> mocked = mockConstruction(PaymentProcessor.class)) { // every object creation is returning a mock from now on PaymentProcessor paymentProcessor = new PaymentProcessor(); when(paymentProcessor.chargeCustomer(anyString(), any(BigDecimal.class))).thenReturn(BigDecimal.TEN); assertEquals(BigDecimal.TEN, paymentProcessor.chargeCustomer("42", BigDecimal.valueOf(42))); } // a real object of the PaymentProcessor is returned System.out.println(new PaymentProcessor().chargeCustomer("42", BigDecimal.valueOf(42))); } } |
Furthermore, as all object constructions for this class are mocked, it doesn't matter which public constructor we use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @Test void mockDifferentObjectConstruction() { try (MockedConstruction<PaymentProcessor> mocked = Mockito.mockConstruction(PaymentProcessor.class)) { PaymentProcessor firstInstance = new PaymentProcessor("PayPal", BigDecimal.TEN); PaymentProcessor secondInstance = new PaymentProcessor(true, "PayPal", BigDecimal.TEN); when(firstInstance.chargeCustomer(anyString(), any(BigDecimal.class))).thenReturn(BigDecimal.TEN); when(secondInstance.chargeCustomer(anyString(), any(BigDecimal.class))).thenReturn(BigDecimal.TEN); assertEquals(BigDecimal.TEN, firstInstance.chargeCustomer("42", BigDecimal.valueOf(42))); assertEquals(BigDecimal.TEN, secondInstance.chargeCustomer("42", BigDecimal.valueOf(42))); assertEquals(2, mocked.constructed().size()); } } |
In the test above, all object creations are mocked, independent of which constructor of PaymentProcessor
we use. We can even inspect how many mock objects were created during a test using the mock controller that is returned from mockConstruction()
.
As the last example, let's take a look at mockConstructionWithAnswer
. Using this method, we can add a default Answer
and additional Answer
s that define our mock's behavior.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Test void mockDifferentObjectConstructionWithAnswer() { try (MockedConstruction<PaymentProcessor> mocked = Mockito.mockConstructionWithAnswer(PaymentProcessor.class, // default answer for the first mock invocation -> new BigDecimal("500.00"), // additional answer for all further mocks invocation -> new BigDecimal("42.00"))) { PaymentProcessor firstInstance = new PaymentProcessor(); PaymentProcessor secondInstance = new PaymentProcessor(); PaymentProcessor thirdInstance = new PaymentProcessor(); assertEquals(new BigDecimal("500.00"), firstInstance.chargeCustomer("42", BigDecimal.ZERO)); assertEquals(new BigDecimal("42.00"), secondInstance.chargeCustomer("42", BigDecimal.ZERO)); assertEquals(new BigDecimal("42.00"), thirdInstance.chargeCustomer("42", BigDecimal.ZERO)); } } |
In our example, we can always return a BigDecimal
as our PaymentProcessor
only has one public method. For classes with multiple methods and different return types, either use the passed invocation
to determine what to return or use the mockConstruction()
that takes a MockInitializer
for type-safe stubbings.
Summary of Mocking Java Constructors
As we can now mock Java constructors and static method calls with Mockito, the need for PowerMock becomes less important. With this feature, you can get rid of PowerMock if you've only used it for those two purposes and rely solely on Mockito.
However, we shouldn't jump into using this feature from now on for every test. Mocking the object construction, e.g., of internally used value objects, is an anti-pattern that we should avoid.
There shouldn't be many valid use cases for this feature whenever we favor constructor injection for our Java classes. With constructor injection, we can pass the collaborators of a class via the constructor during tests. This way, we can prepare a mock of the collaborator before instantiating the class under test and don't have to mock any constructor.
For further practical Mockito advice, consider enrolling in the Hands-On Mocking With Mockito Online Course to learn the ins and outs of the most popular mocking library for JVM applications.
You can find related Mockito articles here:
- Mocking Static Methods with Mockito (Java & Kotlin)
- @Mock vs. @MockBean When Testing Spring Boot Applications
- Maven Setup For Testing Java Applications
The source code for this blog post is available on GitHub.
Have fun mocking your Java constructors,
Philip