Starting with version 3.5.0, Mockito can now mock constructors and return a mock on every object construction. Like mocking static method calls, we can define the scope of returning a mock when constructing an object. While the practical use cases for this feature might be limited, at least that's the next step towards replacing PowerMock with Mockito. Let's take a look at this feature in action.
Configuration Of The InlineMockMaker
This new feature of Mockito is only available if we use an InlineMockMaker
. This interface extends the MockMaker
interface that define Mockito's internal API to create mocks. There are multiple implementations for this interface, and the default one is the SubclassByteBuddyMockMaker
.
To override this 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 reasons 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, put a org.mockito.plugins.MockMaker
file inside src/test/resources/mockito-extensions
with the following content:
1 | mock-maker-inline |
For those who are using the Spring Boot Starter Test to manage the versions of the basic testing libraries, refer to the Mockito setup section of this article.
Mock 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 would use 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()
. As a first argument, this method takes a non-abstract Java class that constructions we're about to mock. 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 Constructors
First, let's come back to the scope of mocking the object construction. Whenever we use the try-with-resources
(which is 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
As we can now mock our 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 which we should avoid.
Having all collaborators of a class injected by its public constructor, this feature's possible use cases are limited.
Are you looking for further practical resources on Mockito? Consider enrolling in my Hands-On With Mockito Online Course to learn the ins and outs of the most popular mocking library for JVM applications.
You can find the source code for this blog post on GitHub.
Have fun mocking your constructors,
Philip