Mockito Annotations with Examples - @Mock, @InjectMocks, @Spy, @Captor, and @ExtendWith

Introduction

Mockito is a popular mocking framework for Java, used to create and configure mock objects for unit testing. Mockito simplifies the process of creating test doubles and helps isolate the code being tested. This blog post will explore some of the key annotations provided by Mockito - @Mock, @InjectMocks, @Spy, @Captor, and @ExtendWith.

Maven Dependencies

To use Mockito with JUnit 5, you need to include the following dependencies in your pom.xml file:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.8.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>4.8.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>

Make sure to add these dependencies to your project's pom.xml to ensure you have the latest versions of Mockito and JUnit.

@Mock

The @Mock annotation is used to create and inject mock objects. It is commonly used to mock dependencies in unit tests.

Example

In this example, we will mock a List object and verify that a method call is made.

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(MockitoExtension.class)
public class MockExampleTest {

    @Mock
    private List<String> mockList;

    @Test
    public void testMock() {
        mockList.add("test");
        verify(mockList).add("test");
    }
}

Key Points:

  • @Mock creates a mock instance of the class.
  • verify is used to verify that the method add was called with the argument "test".

@InjectMocks

The @InjectMocks annotation is used to inject mock objects into the class being tested. It helps to automatically inject all the mocks marked with @Mock or @Spy into the tested object.

Example

In this example, we will mock a Repository and inject it into a Service class. We will then verify that the Service class behaves as expected when the Repository is mocked.

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;

class Service {
    private final Repository repository;

    public Service(Repository repository) {
        this.repository = repository;
    }

    public String fetchData() {
        return repository.getData();
    }
}

interface Repository {
    String getData();
}

@ExtendWith(MockitoExtension.class)
public class InjectMocksExampleTest {

    @Mock
    private Repository mockRepository;

    @InjectMocks
    private Service service;

    @Test
    public void testInjectMocks() {
        when(mockRepository.getData()).thenReturn("mocked data");

        String result = service.fetchData();

        assertEquals("mocked data", result);
    }
}

Key Points:

  • @InjectMocks automatically injects mocks into the service instance.
  • when is used to define the behavior of the mock object.

@Spy

The @Spy annotation is used to create a spy of a real object. Spies allow partial mocking, where you can mock some methods while using real implementations for others.

Example

In this example, we will create a spy for a User class and mock its getName method.

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;

class User {
    public String getName() {
        return "Real Name";
    }
}

@ExtendWith(MockitoExtension.class)
public class SpyExampleTest {

    @Spy
    private User user;

    @Test
    public void testSpy() {
        when(user.getName()).thenReturn("Mocked Name");

        String name = user.getName();

        assertEquals("Mocked Name", name);
    }
}

Key Points:

  • @Spy creates a spy instance of the class.
  • Partial mocking is achieved by defining specific behaviors.

@Captor

The @Captor annotation is used to create an argument captor, which captures argument values passed to mocked methods.

Example

In this example, we will use an argument captor to capture the argument passed to a mocked List method.

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;

import java.util.List;

@ExtendWith(MockitoExtension.class)
public class CaptorExampleTest {

    @Mock
    private List<String> mockList;

    @Captor
    private ArgumentCaptor<String> captor;

    @Test
    public void testCaptor() {
        mockList.add("test");

        verify(mockList).add(captor.capture());

        assertEquals("test", captor.getValue());
    }
}

Key Points:

  • @Captor creates an argument captor instance.
  • Captured arguments can be asserted for verification.

@ExtendWith

The @ExtendWith annotation is used to register extensions in JUnit 5. For Mockito, it is used to initialize Mockito annotations.

Example

In this example, we will use @ExtendWith to initialize Mockito annotations for a test class.

import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;

import java.util.List;

@ExtendWith(MockitoExtension.class)
public class ExtendWithExampleTest {

    @Mock
    private List<String> mockList;

    @Test
    public void testExtendWith() {
        mockList.add("test");
        verify(mockList).add("test");
    }
}

Key Points:

  • @ExtendWith(MockitoExtension.class) initializes Mockito annotations.
  • Ensures proper setup and teardown for Mockito in JUnit 5 tests.

Conclusion

Mockito annotations simplify the creation and management of mock objects in unit tests. By using @Mock, @InjectMocks, @Spy, @Captor, and @ExtendWith, you can write more readable and maintainable test code. These annotations help you focus on testing the behavior of your code without worrying about the complexities of dependency management and object creation. Ensure you include the latest versions of Mockito and JUnit dependencies in your project to take advantage of the latest features and improvements.

Comments