Getting Started with Mockito

November 19, 2020 No comments Mockito Java Library Testing

1. Introduction

Mockito is one of the most popular testing frameworks for Java applications. It provides a way to test the functionality of isolated classes without required dependencies like database connection, filesystem read/write operations or other external services. Mockito API is clean and intuitive. In this article, we are going to present how to start using Mockito in Java projects.

2. Mocking

Mockito framework was created with mocking objects in mind. Mocking is an approach in testing that allows us to check the functionality of a class in isolation. We can mock objects that require database or filesystem connection in runtime. Mocked object's behavior as proxy objects for the existing implementation.

Benefits of Mockito:

  • support for exceptions,
  • mock objects can be created using annotations,
  • support for the return values,
  • mock objects don’t need to be manually written,
  • allow verifying test cases for timeout, counts, and sequence of execution.

3. Setup Mockito

Integrating Mockito in Java projects is easy. The library is available under the Maven Central Repository, so all we need to do is to add a dependency to our project dependency manager:

For Maven projects we need to add the following into pom.xml file:

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

Gradle:

compile group: 'org.mockito', name: 'mockito-core', version: '3.5.9'

Ivy:

<dependency org="org.mockito" name="mockito-core" rev="3.5.9"/>

It will add Mockito's main JAR file with all the required dependencies. Here we used the most recent at a given time version of the mockito-core artifact but if you need another version check the following list of libraries from org.mockito group.

Notice if you want to use Mockito with JUnit 5 you will need to include additional dependency: mockito-junit-jupiter. Mockito works well without additional libraries with JUnit in version 4.

4. Mockito Annotations

Mockito framework comes with several annotations which are a replacement for the usual methods available in the API and make the test code clean and easy to understand.

The Mockito annotations are as follows:

  • @Mock - used to create a mocked instance. Object annotated with @Mock can be used anywhere in the test,
  • @Spy- used for creating a proxy for a provided instance. We can decide which method of spied object will be stubbed,
  • @InjectMocks- used to inject mock instances into the tested object,
  • @Captor - used to create an argument captor.

In order to activate Mockito annotations in test classes we need to use one of the three solutions:

  • the first approach is to annotate the JUnit test class with a MockitoJUnitRunner:
@RunWith(MockitoJUnitRunner.class)
public class TestClass {
    // test cases
}
  • the second solution is to explicitly invoke MockitoAnnotations.initMocks() in the method that will be called before each test:

In JUnit 4:

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

In JUnit 5:

@BeforeEach
public void setUp() {
    MockitoAnnotations.initMocks(this);
}
  • finally in JUnit from version 4.7 we can use a dedicated rule that will initialize mocks annotated with @Mock before each test method - MockitoJUnit.rule():
public class TestClass {
    @Rule 
    public MockitoRule rule = MockitoJUnit.rule();

    // test cases
}

5. JUnit 4 Mockito Example

It is always better to understand how a given library works by presenting a specific case from real life. In this example we will use a simple shopping basket implementation with the following classes:

  • Product - contains the name and price of the product,
package com.frontbackend.libraries.mockito.model;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class Product {

    private final String name;
    private final double price;

}
  • BasketEntry - this class will represent the product in basket:
package com.frontbackend.libraries.mockito.model;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class BasketEntry {

    private final Product product;
    private final double quantity;
}
  • Basket - shopping basket that contains list of entries:
package com.frontbackend.libraries.mockito.model;

import java.util.ArrayList;
import java.util.List;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class Basket {

    private final List<BasketEntry> entries = new ArrayList<>();
}
  • BasketService responsible for adding products to the shopping basket and calculating the total amount of a shopping basket:
package com.frontbackend.libraries.mockito.service;

import com.frontbackend.libraries.mockito.model.Basket;
import com.frontbackend.libraries.mockito.model.BasketEntry;
import com.frontbackend.libraries.mockito.model.Product;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class BasketService {

    private final Basket basket;

    public void addProductToBasket(Product product, double quantity) {
        BasketEntry basketEntry = new BasketEntry(product, quantity);
        basket.getEntries()
              .add(basketEntry);
    }

    public double getTotalAmount() {
        return basket.getEntries()
                     .stream()
                     .mapToDouble(this::getBasketEntryPrice)
                     .sum();
    }

    private double getBasketEntryPrice(BasketEntry basketEntry) {
        return basketEntry.getProduct()
                          .getPrice()
                * basketEntry.getQuantity();
    }
}

We want to test if BasketService correctly calculates the total amount of the products in the shopping basket. Let's check how our sample test:

package com.frontbackend.libraries.mockito.service;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;

import com.frontbackend.libraries.mockito.model.Basket;
import com.frontbackend.libraries.mockito.model.Product;

@RunWith(MockitoJUnitRunner.class)
public class BasketServiceTest {

    @Spy
    private Basket basket;

    @Mock
    private Product banana;

    @InjectMocks
    private BasketService basketService;

    @Test
    public void shouldCountTotalAmountCorrectly() {
        // Given
        Product milk = new Product("Milk", 10.00);
        when(banana.getPrice()).thenReturn(2.00);

        // When
        basketService.addProductToBasket(banana, 2.5);
        basketService.addProductToBasket(milk, 1.5);
        double totalAmount = basketService.getTotalAmount();

        // Then
        assertEquals("Total amount should be 20.0", 20.0, totalAmount, 0);
        verify(basket, times(3)).getEntries(); // adding 2 products + counting total price
        verify(banana, never()).getName(); // method getName() was never called
    }
}

There are some aspects here that need to be explained:

  • basket is annotated with @Spy - we just want to count method executions,
  • banana is annotated with @Mock - for this object we want to mock implementation of getPrice() method,
  • basketService is annotated with @InjectMocks because we want to create a BasketService instance with mocked basket object,
  • Product milk = new Product("Milk", 10.00); - milk object is real,
  • when(banana.getPrice()).thenReturn(2.00); - getPrice() method for banana will return 2.00,
  • totalAmount should be 20.0, because: (10.00 times 1.5 Milk = 15) + (2.00 times 2.5 Banana = 5),
  • basket.getEntries() method should be called 3 times,
  • verify(banana, never()).getName() - basket service never calls getName() method on products.

6. Conclusion

In this article, we presented how to start using Mockito in Java projects. We showed a brief description of common annotations that could be used in tests. The sample and not very complex application, with Mockito used in the JUnit test, should be useful for everyone unfamiliar with this library.

As usual, the code used in this article can be found over on GitHub.

{{ message }}

{{ 'Comments are closed.' | trans }}