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.
Let's use the UserService.java and UserRepository.java classes from our previous article on Mocking external dependencies in Unit Tests using Mockito framework.
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