Introduction
The doAnswer()
method in Mockito is used to specify custom behavior for a method call on a mock object. This is particularly useful when you need to perform more complex operations or handle specific cases that cannot be easily managed with doReturn()
, doThrow()
, or when()
. It allows you to define what should happen when a method on the mock object is called.
When to Use doAnswer()
Use doAnswer()
when you need to:
- Perform complex logic: When the behavior of a method cannot be easily defined by simple return values or exceptions.
- Capture method arguments: When you need to inspect or modify the arguments passed to the mock method.
- Invoke a callback or listener: When your method needs to trigger some action in response to being called.
- Combine multiple behaviors: When you need to perform several actions in response to a single method call.
How doAnswer() Works
The doAnswer()
method allows you to define a custom Answer
implementation that specifies what should happen when the method is called. This Answer
can access the arguments passed to the method, perform any necessary logic, and return a value or throw an exception.
Example Scenario
We will create a UserService
class that has a dependency on a UserRepository
. Our goal is to test the UserService
methods using the doAnswer()
method in Mockito to handle custom behavior.
UserService and UserRepository Classes
First, create the User
class, the UserRepository
interface, and the UserService
class.
public class User {
private String name;
private String email;
// Constructor, getters, and setters
public User(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public interface UserRepository {
void saveUser(User user);
User findUserByEmail(String email);
}
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void registerUser(String name, String email) {
User user = new User(name, email);
userRepository.saveUser(user);
}
public User getUserByEmail(String email) {
return userRepository.findUserByEmail(email);
}
}
JUnit 5 Test Class with Mockito
Create a test class for UserService
using JUnit 5 and Mockito.
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
public void testRegisterUser() {
// Given
String name = "Ramesh Fadatare";
String email = "ramesh.fadatare@example.com";
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
User user = invocation.getArgument(0);
assertEquals("Ramesh Fadatare", user.getName());
assertEquals("ramesh.fadatare@example.com", user.getEmail());
return null;
}
}).when(userRepository).saveUser(any(User.class));
// When
userService.registerUser(name, email);
// Then
verify(userRepository).saveUser(any(User.class));
}
@Test
public void testGetUserByEmail() {
// Given
String email = "anita.patil@example.com";
User user = new User("Anita Patil", email);
doAnswer(new Answer<User>() {
@Override
public User answer(InvocationOnMock invocation) throws Throwable {
String email = invocation.getArgument(0);
if ("anita.patil@example.com".equals(email)) {
return user;
} else {
return null;
}
}
}).when(userRepository).findUserByEmail(anyString());
// When
User result = userService.getUserByEmail(email);
// Then
assertNotNull(result);
assertEquals("Anita Patil", result.getName());
assertEquals(email, result.getEmail());
}
}
Explanation
Creating Mocks with
@Mock
:- The
@Mock
annotation creates a mock instance of theUserRepository
interface. This mock instance can be used to simulate the behavior of theUserRepository
in a controlled way.
- The
Injecting Mocks with
@InjectMocks
:- The
@InjectMocks
annotation injects the mockUserRepository
into theUserService
instance to provide a controlled test environment. This allows theUserService
methods to be tested in isolation from the actualUserRepository
implementation.
- The
Defining Custom Behavior with
doAnswer()
:- The
doAnswer(new Answer<Void>() { ... }).when(userRepository).saveUser(any(User.class));
method configures the mockUserRepository
to execute custom behavior when thesaveUser
method is called with anyUser
object. - Inside the
answer
method, we retrieve the arguments passed to thesaveUser
method and perform assertions to verify the user's name and email. This allows us to check that theregisterUser
method of theUserService
class correctly interacts with theUserRepository
. - Similarly, the
doAnswer(new Answer<User>() { ... }).when(userRepository).findUserByEmail(anyString());
method configures the mockUserRepository
to return a specificUser
object when thefindUserByEmail
method is called with the specified email. This allows us to check that thegetUserByEmail
method of theUserService
class correctly interacts with theUserRepository
.
- The
Verifying Interactions with
verify()
:- The
verify(userRepository).saveUser(any(User.class));
method checks if thesaveUser
method was called on theUserRepository
with anyUser
object. This ensures that theregisterUser
method of theUserService
class interacts with theUserRepository
correctly.
- The
Use Cases
Use Case 1: Performing Complex Logic
@Test
public void testComplexLogicInMethodCall() {
// Given
String email = "ravi.kumar@example.com";
User user = new User("Ravi Kumar", email);
doAnswer(new Answer<User>() {
@Override
public User answer(InvocationOnMock invocation) throws Throwable {
String email = invocation.getArgument(0);
// Perform complex logic
if ("ravi.kumar@example.com".equals(email)) {
return user;
} else {
return null;
}
}
}).when(userRepository).findUserByEmail(anyString());
// When
User result = userService.getUserByEmail(email);
// Then
assertNotNull(result);
assertEquals("Ravi Kumar", result.getName());
assertEquals(email, result.getEmail());
}
Use Case 2: Capturing and Modifying Arguments
@Test
public void testCaptureAndModifyArguments() {
// Given
String name = "Anjali Sharma";
String email = "anjali.sharma@example.com";
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
User user = invocation.getArgument(0);
user.setName(user.getName().toUpperCase());
return null;
}
}).when(userRepository).saveUser(any(User.class));
// When
userService.registerUser(name, email);
// Then
verify(userRepository).saveUser(argThat(user -> "ANJALI SHARMA".equals(user.getName()) && email.equals(user.getEmail())));
}
Use Case 3: Invoking a Callback
@Test
public void testInvokeCallback() {
// Given
String name = "Rajesh Verma";
String email = "rajesh.verma@example.com";
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
// Invoke a callback
Runnable callback = invocation.getArgument(1);
callback.run();
return null;
}
}).when(userRepository).saveUser(any(User.class), any(Runnable.class));
// When
userService.registerUser(name, email);
// Then
verify(userRepository).saveUser(any(User.class), any(Runnable.class));
}
Conclusion
The doAnswer()
method in Mockito simplifies the configuration of custom behavior for method calls on mock objects for unit testing. By using doAnswer()
, you can handle complex scenarios and perform specific actions when methods on mock objects are called.
Related Mockito Methods
Mockito mock()
Mockito spy()
Mockito when()
Mockito thenThrow()
Mockito verify()
Mockito times()
Mockito never()
Mockito any()
Mockito eq()
Mockito inOrder()
Mockito doReturn()
Mockito doThrow()
Mockito doAnswer()
Mockito timeout()
Mockito ArgumentMatchers
Comments
Post a Comment
Leave Comment