An Introduction to the Mockito Framework

In today’s world of agile development, writing unit tests is vital and helpful in producing high-quality code. Unit tests ensure that the code complies with quality standards and that issues are identified even before the deployment. Java offers various testing frameworks to make the lives of developers easier, with Mockito being one of the well-known ones. In this article, we will cover an introduction to the Mockito Framework, this would enable us to understand the core concepts of the Mockito Framework along with some practical implementation. So, let’s begin!

If you are not familiar with unit tests with JUnit and Mockito framework, it is recommended to go through our JUnit series first.

What is Mocking?

The word “Mocking” refers to the behavior of copying or mimicking a real object. The same concept applies in the field of unit testing, where mock objects are used to mock the behavior of real objects. The mock objects verify the behavior of the other object. With the technique of mocking, we can test the behavior of the module or service by isolating it from the other functionalities of the code. The mocking technique is also helpful in a scenario, where the other dependent module or service, or object is not yet ready.

Consider a use-case where some service reads data from the database layer and returns data to the end-user in an organized manner. The task of the tester is to verify the end result that comes after the arrangement of the data into the particular DTO. In this particular scenario, the tester would avoid setting up a whole database as it would be time-consuming, also additional resources would be required to set up a test instance. As a result, the tester will mock the database layer results and concentrate exclusively on the real task, namely the retrieval of data in a particular manner.

Hold on! This is just one example, over the next few tutorials, we will cover more scenarios and use-cases.

What is a Mockito Framework?

Mockito is an open-source testing framework for Java. The framework allows the creation of mock objects in automated unit tests for the purpose of test-driven development (TDD) or behavior-driven development (BDD).

>> Don’t worry, we will cover TDD and BDD some other time

The following are some of the important features of the Mockito Framework:

  • Mockito allows writing test methods to be compatible with the “arrange/act/assert” approach.
  • Mockito comes along with some annotations to avoid boilerplate code.
  • Creates mocks of concrete classes and interfaces using annotations.
  • Provides verification support for an exact number of invocations.
  • Provides a wide range of argument matches for verifications along with the support of making a custom argument matcher.
  • Supports clean verification errors. This helps developers figure out their mistakes while writing unit tests.
  • Mockito provides easy and relaxed unit test writing by providing developers special constructs to verify only the data type and not any specific value. For eg. anyInt() and not any particular integer value.

Read more about Mockito here.

Important terminologies used in Mocking Frameworks

Before we get our hands dirty, let’s go over some core terminologies to understand some basic concepts:

Test Doubles

Whenever you read about testing, you will often encounter the term Test Doubles. In simple terms, it is a generic term for any case where you replace a production object for testing purposes. Test doubles enable us to completely test the behavior of the code under test by isolating it. There are several types of test doubles available which we will look at it in a while.

Stub

Stub (noun) by definition is a short part remaining after the rest has been removed or used up. It typically serves the same purpose during the testing phase of software development. It fills in specific, fixed, and hard-coded values to replace the values coming outside of the test. For example, consider an airline management system, where users book their airline tickets; this whole process involves a series of steps by fetching a few details from the database or any third-party service. While testing, usually QAs make test stubs for the ticket cost to abstract the details of the intermediary steps. The main purpose of test stubs is to seamlessly test the main purpose of the function using arbitrary values.

Fake

Fake (noun) by definition is a thing that is not genuine. As per the definition, fake objects do the work, but with a different implementation. They take some shortcuts to achieve a similar result. Please note this is not the best option for production. These are suitable to test the rest APIs by faking out the authentication mechanism. Usually, REST APIs are protected using third-party servers like Keycloak/LDAP/Oauth2, etc. where tokens are verified and also expire after a certain time. In this scenario, fake objects are useful to only mock the behavior of the authentication and then test the rest of the main features i.e. The working of the API.

Spy

Spy (noun) by definition is one who keeps a secret watch on a person or thing to obtain information. Just like the definition, in testing frameworks spy acts as a secret agent and keeps track of its state and how the system uses it.  Spies are a special kind of stubs that also records information about how they are being called and how much time. These are useful in writing test cases for complex scenarios.

Mock

Mock (noun) by definition is one that is an object of derision or scorn. While in testing terminology mock is the intimation of a programmed module/method with some pre-defined behavior or values.  This is helpful in implementing a real subsystem in a controlled manner. There are many use cases of using mock objects, one example is a scenario where AOP is implemented for every rest call to log details before its actual implementation. In this scenario, testers can mock up the functions for the AOP logic as it would be time-consuming and irrelevant to test for all the APIs.

Common Annotations used in Mockito Framework

Mockito allows us to create test doubles for Mock and Spy. Let’s have a look at the following annotations:

@Mock

It is the most widely used annotation in the Mockito framework, as the name suggests it creates mocked objects. If you are already interested to find more examples of how to use the @Mock annotation, have a look at the tutorial that is called Getting Started with Mockito @Mock and @InjectMocks.

@Spy

It is another kind of test double, this annotation creates an object that spies on another real object.

Application of Mockito

Project Setup

We will be using JUnit as a unit testing framework. Since Mockito isn’t tied to JUnit, you can still follow along even if you’re using a different framework.

Let’s assume you have a Maven, Spring Boot Project. Refer to this article for more details. To begin working with the Mockito framework, all we need to do is to include the following dependency to our Maven project:

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

Building a simple Example

Just for the sake of illustration, consider a simple Student Management System, where we have a StudentService and StudentRepository class that returns a list of all students. StudentRepository is injected into the StudentService class to be used as a dependency. Following is a code snippet:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.appsdeveloper</groupId>
    <artifactId>junit-examples</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>junit-examples</name>
    <description>junit-examples</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>4.7.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

StudentService

package com.appsdeveloper.junitexamples.service;

import com.appsdeveloper.junitexamples.model.Student;
import com.appsdeveloper.junitexamples.repository.StudentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class StudentService {

    @Autowired
    private StudentRepository studentRepository;

    public List<Student> getAllStudents() {
        return studentRepository.getAllStudents();
    }

}

StudentRepository

package com.appsdeveloper.junitexamples.repository;

import com.appsdeveloper.junitexamples.model.Student;
import org.springframework.stereotype.Repository;

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

@Repository
public class StudentRepository {


    public List<Student> getAllStudents() {
        return getStudents();
    }

    private List<Student> getStudents() {
        Student student = new Student();
        student.setId(1);
        List<Student> students = new ArrayList<>();
        students.add(student);
        return students;
    }
}

Please note we have created repository and service classes just for the sake of testing purposes.

StudentServiceTest

Now, create a test class for the service layer inside the test package.

package com.appsdeveloper.junitexamples.service;

import com.appsdeveloper.junitexamples.model.Student;
import com.appsdeveloper.junitexamples.repository.StudentRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

public class StudentServiceTest {

    @Mock
    private StudentRepository studentRepository;

    @InjectMocks
    private StudentService studentService;

    @BeforeEach
    public void init() {
        //Initializes objects annotated with Mockito annotations for given testClass: @Mock, @Spy, @Captor, @InjectMocks
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void testGetAllStudents() {
        // Arrange
        when(studentRepository.getAllStudents()).thenReturn(getAllStudents());

        // Act
        List<Student> students = studentService.getAllStudents();
 
        // Assert
        assertEquals(1, students.size());
        assertEquals(1l, students.get(0).getId());
    }

    public List<Student> getAllStudents() {
        return getStudents();
    }

    private List<Student> getStudents() {
        Student student = new Student();
        student.setId(1);
        List<Student> students = new ArrayList<>();
        students.add(student);
        return students;
    }

}

The key points of the above test class are as under:

  • It uses @Mock annotation to mimic the behavior of StudentRpository class.
  • @InjectMock ensures the injection of a mock object into the object under test. In our case, it is the StudentService class.
  • @BeforeEach method is run before each test case, and inside the method body, it is instructed to initialize mocks every time.
  • In the above test class, there is only one test case written against a service method getAllStudents().
  • Mockito uses the when-then approach to instruct the mocked objects to behave in a particular way. In our example, we have returned a defined set of students when a studentRepository.getAllStudents() method is triggered.
  • The call to the method under test is done.
  • Finally, a test case uses the assertions to verify the behavior.

Conclusion

This blog post covered an introduction to the Mockito Framework, one of the most prominent testing frameworks of Java. We have also looked at what test doubles are along with the various test double types. We have looked at the basic usage and creation of Mock objects in the simplest possible way. Since Mockito provides us a way to leverage our coding standards and improve code quality, it’s on us to utilize it in the best possible way!

If you find this article helpful, don’t forget to share your feedback or thoughts in the comments section.

Happy Learning!