Introduction
In this chapter, we will learn about argument captors using Mockito. Argument captors allow you to capture and inspect the arguments passed to mocked methods. This is useful for verifying that the methods were called with the expected arguments, and for gaining deeper insights into how your code interacts with its dependencies.
What are Argument Captors?
Argument captors enable you to capture the arguments passed to a method when it is called. You can then perform assertions or inspections on these captured arguments to ensure they meet your expectations.
Using Argument Captors
Mockito provides the ArgumentCaptor
class to help capture method arguments. You can use it to capture arguments for verification purposes.
Example: UserService Class and UserServiceTest Class
Class Under Test: UserService
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void registerUser(String username, String email) {
User user = new User(username, email);
userRepository.save(user);
}
}
Supporting Class: UserRepository
public class UserRepository {
public void save(User user) {
// Save the user to the database
}
}
Supporting Class: User
public class User {
private final String username;
private final String email;
public User(String username, String email) {
this.username = username;
this.email = email;
}
public String getUsername() {
return username;
}
public String getEmail() {
return email;
}
}
Test Class: UserServiceTest
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import static org.junit.jupiter.api.Assertions.*;
public class UserServiceTest {
@Test
void testRegisterUser_CapturingArguments() {
// Create a mock object of UserRepository
UserRepository userRepository = mock(UserRepository.class);
// Create an instance of UserService with the mock dependency
UserService userService = new UserService(userRepository);
// Register a user
userService.registerUser("john_doe", "john@example.com");
// Capture the argument passed to the save method
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
verify(userRepository).save(userCaptor.capture());
// Get the captured argument
User capturedUser = userCaptor.getValue();
// Assert the captured argument
assertEquals("john_doe", capturedUser.getUsername());
assertEquals("john@example.com", capturedUser.getEmail());
}
}
Explanation
-
Creating Mocks:
UserRepository userRepository = mock(UserRepository.class);
This line creates a mock object of the
UserRepository
class. -
Creating an Instance of UserService:
UserService userService = new UserService(userRepository);
This line creates an instance of the
UserService
class with the mock dependency. -
Registering a User:
userService.registerUser("john_doe", "john@example.com");
This line calls the
registerUser
method on theUserService
instance with the specified username and email. -
Capturing the Argument:
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class); verify(userRepository).save(userCaptor.capture());
These lines create an
ArgumentCaptor
for theUser
class and use it to capture the argument passed to thesave
method of theUserRepository
mock. -
Getting the Captured Argument:
User capturedUser = userCaptor.getValue();
This line retrieves the captured argument from the
ArgumentCaptor
. -
Asserting the Captured Argument:
assertEquals("john_doe", capturedUser.getUsername()); assertEquals("john@example.com", capturedUser.getEmail());
These lines assert that the captured argument has the expected username and email.
Capturing Multiple Arguments
You can capture multiple arguments using the getAllValues
method of the ArgumentCaptor
.
Example: Capturing Multiple Arguments
Modified UserService Class
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public void registerUser(String username, String email) {
User user = new User(username, email);
userRepository.save(user);
emailService.sendWelcomeEmail(email);
}
}
Supporting Class: EmailService
public class EmailService {
public void sendWelcomeEmail(String email) {
// Send a welcome email
}
}
Modified UserServiceTest Class
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import static org.junit.jupiter.api.Assertions.*;
public class UserServiceTest {
@Test
void testRegisterUser_CapturingMultipleArguments() {
// Create mock objects of UserRepository and EmailService
UserRepository userRepository = mock(UserRepository.class);
EmailService emailService = mock(EmailService.class);
// Create an instance of UserService with the mock dependencies
UserService userService = new UserService(userRepository, emailService);
// Register a user
userService.registerUser("john_doe", "john@example.com");
// Capture the arguments passed to the save and sendWelcomeEmail methods
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
ArgumentCaptor<String> emailCaptor = ArgumentCaptor.forClass(String.class);
verify(userRepository).save(userCaptor.capture());
verify(emailService).sendWelcomeEmail(emailCaptor.capture());
// Get the captured arguments
User capturedUser = userCaptor.getValue();
String capturedEmail = emailCaptor.getValue();
// Assert the captured arguments
assertEquals("john_doe", capturedUser.getUsername());
assertEquals("john@example.com", capturedUser.getEmail());
assertEquals("john@example.com", capturedEmail);
}
}
Explanation
-
Capturing Multiple Arguments:
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class); ArgumentCaptor<String> emailCaptor = ArgumentCaptor.forClass(String.class); verify(userRepository).save(userCaptor.capture()); verify(emailService).sendWelcomeEmail(emailCaptor.capture());
These lines create
ArgumentCaptor
instances for theUser
andString
classes and use them to capture the arguments passed to thesave
andsendWelcomeEmail
methods. -
Getting the Captured Arguments:
User capturedUser = userCaptor.getValue(); String capturedEmail = emailCaptor.getValue();
These lines retrieve the captured arguments from the
ArgumentCaptor
instances. -
Asserting the Captured Arguments:
assertEquals("john_doe", capturedUser.getUsername()); assertEquals("john@example.com", capturedUser.getEmail()); assertEquals("john@example.com", capturedEmail);
These lines assert that the captured arguments have the expected values.
Conclusion
Argument captors in Mockito provide a powerful way to capture and inspect the arguments passed to mocked methods. By using ArgumentCaptor
, you can capture single or multiple arguments and perform assertions or inspections on them. This helps you ensure that your code interacts with its dependencies as expected and provides deeper insights into the behavior of your code. Understanding and utilizing argument captors will help you write more effective and maintainable tests.