406

Mock Java Constructors and Their Object Creation With Mockito

 3 years ago
source link: https://rieckpil.de/mock-java-constructors-and-their-object-creation-with-mockito/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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 :

<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:

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:

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.

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.

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:

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:

@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 Answers that define our mock's behavior.

@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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK