Introduction
The @Captor
annotation in Mockito is used to create an argument captor, which is a special type of object that can capture argument values passed to mock methods. This is particularly useful when you want to verify that a method was called with specific arguments or to inspect the arguments passed to a mock method. In this tutorial, we will demonstrate step by step how to use the @Captor
annotation in Mockito.
Maven Dependencies
To use Mockito with JUnit 5, add the following dependencies to 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>
Example Scenario
We will create a UserService
class that has a dependency on a UserRepository
. Our goal is to test the UserService
methods using Mockito's @Captor
annotation to capture and verify the arguments passed to the UserRepository
methods.
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.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
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;
@Captor
private ArgumentCaptor<User> userCaptor;
@Test
public void testRegisterUser() {
// Given
String name = "John Doe";
String email = "john.doe@example.com";
// When
userService.registerUser(name, email);
// Then
verify(userRepository).saveUser(userCaptor.capture());
User capturedUser = userCaptor.getValue();
assertEquals(name, capturedUser.getName());
assertEquals(email, capturedUser.getEmail());
}
@Test
public void testGetUserByEmail() {
// Given
String email = "jane.doe@example.com";
User mockUser = new User("Jane Doe", email);
when(userRepository.findUserByEmail(email)).thenReturn(mockUser);
// When
User result = userService.getUserByEmail(email);
// Then
assertNotNull(result);
assertEquals("Jane Doe", result.getName());
assertEquals(email, result.getEmail());
verify(userRepository).findUserByEmail(email);
}
}
Explanation
Annotations:
@ExtendWith(MockitoExtension.class)
: Integrates Mockito with JUnit 5, enabling the use of Mockito annotations.@Mock
: Creates a mock instance of theUserRepository
interface. This mock will be used to simulate the behavior of the actual repository without needing a concrete implementation.@InjectMocks
: Injects the mockUserRepository
into theUserService
instance.@Captor
: Creates anArgumentCaptor
for capturingUser
objects.
testRegisterUser():
- Given: Sets up the name and email for a new user.
- When: Calls the
registerUser
method on theUserService
instance. - Then:
- Verifies that the
saveUser
method was called on theUserRepository
with aUser
object. - Captures the
User
object passed to thesaveUser
method using theuserCaptor
. - Asserts that the captured user's name and email are correct.
- Verifies that the
testGetUserByEmail():
- Given: Sets up a mock
User
object and configures theUserRepository
to return the mock user when thefindUserByEmail
method is called. - When: Calls the
getUserByEmail
method on theUserService
instance. - Then:
- Asserts that the returned user is not null and that the name and email are correct.
- Verifies that the
findUserByEmail
method was called on theUserRepository
with the expected email.
In the above code example, the UserRepository
interface does not have an implementation class. This is intentional because Mockito is used to create a mock of the UserRepository
. The mock simulates the behavior of the UserRepository
without needing an actual implementation.
When using Mockito, you do not need a concrete implementation of the interface you are mocking. Mockito will generate a mock object that implements the interface and allows you to specify the behavior of its methods.
Conclusion
The @Captor
annotation in Mockito allows you to capture and inspect arguments passed to mock methods, providing more control and insight into your unit tests. By using @Captor
, you can verify that methods are called with the correct arguments and inspect the values passed to them. This step-by-step guide demonstrated how to effectively use the @Captor
annotation in your unit tests, covering different scenarios to ensure comprehensive testing of the UserService
class.
Comments
Post a Comment
Leave Comment