How to mock a dependency using Mockito in JUnit

 How to mock and inject dummy implementation using Mockito for Unit Testing?

In this article we will explore the various ways of defining a mock implementation for a dependency class in Unit Tests.

For writing Unit Tests, we have to decouple the tested class from any of its dependencies. Mockito provides us an easy way to define mock implementation and control response from a dependency class/method.


UserService.java

  package com.devnips.mockitojunit5.service;

import com.devnips.mockitojunit5.model.User;
import com.devnips.mockitojunit5.repository.UserRepository;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    /**
     * Save a user in database
     *
     * @param id
     * @return
     */
    public Optional<User> findById(Long id) {
        if (id == null) {
            throw new RuntimeException("Id is required");
        }
        return userRepository.findById(id);
    }
}
  
UserRepository.java

  package com.devnips.mockitojunit5.repository;

import com.devnips.mockitojunit5.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}
  
Now let's see various options on how to write a Unit Test for UserServer.java class by mocking UserRepository.java dependency.

1. Simple Mock

In this method we create a mock object programatically by using the 

public static <T> T mock(Class<T> classToMock)

method of Mockito.java class.

UserServiceSimpleMockTest.java

  import com.devnips.mockitojunit5.model.User;
import com.devnips.mockitojunit5.repository.UserRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.Optional;

class UserServiceSimpleMockTest {

    @Test
    void simple_mock() {
        // Create a mock object using Mockito.
        UserRepository mockedUserRepository = Mockito.mock(UserRepository.class);

        // Inject mock implementation of UserRepository as dependency to UserService method.
        UserService userService = new UserService(mockedUserRepository);

        // define expectation from the findById() method of UserRepository mock object
        Mockito.doReturn(Optional.of(new User(100L)))
                .when(mockedUserRepository)
                .findById(100L);

        // When the tested method is invoked.
        Optional<User> result = userService.findById(100L);

        // Then the dummy User object should be returned.
        Assertions.assertTrue(result.isPresent());
        Assertions.assertEquals(100L, result.get().getId());
    }
}

  

2. Mock with default response for methods.

Mockito provides an overloaded implementation of the mock() method which accepts an Answer object to define default expectation from mock methods.
public static <T> T mock(Class<T> classToMock, Answer defaultAnswer)

We use the above method to create a mock object as shown below.

UserServiceMockWithAnswerTest.java

  import com.devnips.mockitojunit5.model.User;
import com.devnips.mockitojunit5.repository.UserRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.Optional;

/**
 * Here we create a simple mock object from Mockito and define default Answer for that mock object.
 */
class UserServiceMockWithAnswerTest {

    @Test
    void simple_mock() {
        // Create a mock object and set default answer for all methods.
        // This Answer implementation will be invoked if no expectation is defined for a method.
        UserRepository mockedUserRepository =
                Mockito.mock(UserRepository.class, new Answer() {
                    @Override
                    public Object answer(InvocationOnMock invocationOnMock) {
                        return Optional.of(new User(200L));
                    }
                });

        // Inject mock implementation of UserRepository as dependency to UserService method.
        UserService userService = new UserService(mockedUserRepository);

        // When the tested method is invoked.
        Optional<User> result = userService.findById(100L);

        // Then the default User object defined during creation of mock should be returned.
        Assertions.assertTrue(result.isPresent());
        Assertions.assertEquals(200L, result.get().getId());

        // define custom response for the findById() method of UserRepository mock object.
        // This will override the default Answer implementation.
        Mockito.doReturn(Optional.of(new User(100L)))
                .when(mockedUserRepository)
                .findById(100L);

        // Now when the tested method is invoked.
        Optional<User> customResult = userService.findById(100L);

        // Then the custom User object defined in doReturn method should be returned.
        Assertions.assertTrue(customResult.isPresent());
        Assertions.assertEquals(100L, customResult.get().getId());
    }
}
  


3. Using annotations


Mockito provides a JUnit extension class MockitoExtension that can be used to enable annotation processing and notify Mockito to create Mocks using annotations.

Here is a simple example of creating mock objects using annotations. This annotation processing method fits very well with Spring dependency injection.

UserServiceWithMockitoTest.java

  import com.devnips.mockitojunit5.model.User;
import com.devnips.mockitojunit5.repository.UserRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Optional;

/**
 * This test class shows how to mock a dependency class using Mockito annotations.
 * The below @ExtendWith annotation enabled the Mockito extension, which is required for processing annotations.
 */
@ExtendWith(MockitoExtension.class)
class UserServiceWithMockitoTest {

    /**
     * @Mock is an annotation provided by Mockito that creates a dummy implementation of the given class or interface.
     * Mockito uses Java's proxy pattern to create the dummy class.
     */
    @Mock
    private UserRepository userRepository;

    /**
     * @InjectMocks is a Mockito annotations that tells Mockito to create an actual instance of the given class and
     * also inject any dependencies that are defined with @Mock annotation.
     */
    @InjectMocks
    private UserService userService;

    @Test
    void findById_existing_id() {
        // Here we are defining the behaviour of given method in our Mock implementation.
        // We are telling Mockito to return a User object when `findById()` method of userRepository object is called
        // with parameter as 100L
        Mockito.doReturn(Optional.of(new User(100L)))
                .when(userRepository)
                .findById(100L);

        // When the tested method is invoked.
        Optional<User> result = userService.findById(100L);

        // Then the defined User object should be returned.
        Assertions.assertTrue(result.isPresent());
        Assertions.assertEquals(100L, result.get().getId());
    }

    @Test
    void findById_non_existent_id() {
        Mockito.doReturn(Optional.empty())
                .when(userRepository)
                .findById(200L);

        Optional<User> result = userService.findById(200L);

        Assertions.assertFalse(result.isPresent());
    }
}
  


0 comments:

Post a Comment