[Java Backend Zero to Hello] BÀI 7.2: MOCKITO
📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 7: Testing & DevOps Cơ Bản | Bài 72/86
BÀI 7.2: MOCKITO
Mục tiêu
- Sử dụng Mockito để mock dependency
- Viết test với Mock, Stub, Spy
- Verify behavior
- Sử dụng với Spring Boot
1. MOCKITO LÀ GÌ?
Mockito là framework mock phổ biến nhất cho Java, giúp:
- Tạo mock object cho dependency
- Stub hành vi của mock
- Verify method được gọi
2. DEPENDENCY
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
Hoặc dùng spring-boot-starter-test (bao gồm sẵn).
3. TẠO MOCK
3.1 @Mock
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldFindUser() {
// Stub
when(userRepository.findById(1L))
.thenReturn(Optional.of(new User(1L, "An")));
// Act
User user = userService.findById(1L);
// Assert
assertThat(user.getName()).isEqualTo("An");
}
}
3.2 mock() static
UserRepository mock = mock(UserRepository.class);
4. STUB
4.1 thenReturn
when(userRepository.findById(1L))
.thenReturn(Optional.of(new User(1L, "An")));
when(userRepository.findById(999L))
.thenReturn(Optional.empty());
4.2 thenThrow
when(userRepository.findById(1L))
.thenThrow(new RuntimeException("DB error"));
4.3 thenAnswer
when(userRepository.save(any(User.class)))
.thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(1L);
return user;
});
4.4 Chaining
when(userRepository.findById(1L))
.thenReturn(Optional.of(user1))
.thenReturn(Optional.of(user2))
.thenThrow(new RuntimeException("Not found"));
5. ARGUMENT MATCHERS
// any()
when(userRepository.findById(any(Long.class)))
.thenReturn(Optional.of(user));
// anyString()
when(userRepository.findByEmail(anyString()))
.thenReturn(Optional.of(user));
// eq()
when(userRepository.findById(eq(1L)))
.thenReturn(Optional.of(user));
// isNull / notNull
when(userRepository.findById(isNull()))
.thenThrow(new IllegalArgumentException());
// Custom matcher
when(userRepository.findByName(argThat(name -> name.startsWith("A"))))
.thenReturn(users);
6. VERIFY
6.1 Verify method được gọi
userService.create(request);
verify(userRepository).save(any(User.class));
verify(userRepository, times(1)).save(any(User.class));
verify(userRepository, never()).delete(any(User.class));
verify(userRepository, atLeastOnce()).findAll();
6.2 Verify với argument
verify(userRepository).findById(1L);
verify(userRepository).findById(eq(1L));
verify(userRepository).save(argThat(user -> user.getName().equals("An")));
6.3 Verify thứ tự
InOrder inOrder = inOrder(userRepository, emailService);
inOrder.verify(userRepository).save(user);
inOrder.verify(emailService).sendWelcome(user);
6.4 Verify không có interaction khác
verifyNoMoreInteractions(userRepository);
verifyNoInteractions(emailService);
7. SPY
Spy wrap một object thật, cho phép verify mà vẫn gọi method thật.
@Spy
private List<String> list = new ArrayList<>();
@Test
void shouldAddToList() {
list.add("An");
list.add("Bình");
assertThat(list).hasSize(2);
verify(list).add("An");
}
8. @CAPTOR
Bắt argument được truyền vào mock.
@Captor
private ArgumentCaptor<User> userCaptor;
@Test
void shouldSaveUser() {
userService.create(request);
verify(userRepository).save(userCaptor.capture());
User saved = userCaptor.getValue();
assertThat(saved.getName()).isEqualTo("An");
}
9. @INJECTMOCKS
Tự động inject mock vào object cần test.
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
}
10. VÍ DỤ HOÀN CHỈNH
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@Test
void shouldCreateUser() {
// Arrange
CreateUserRequest request = new CreateUserRequest("An", "an@example.com", 25);
User savedUser = new User(1L, "An", "an@example.com", 25);
when(userRepository.existsByEmail("an@example.com")).thenReturn(false);
when(userRepository.save(any(User.class))).thenReturn(savedUser);
// Act
UserResponse response = userService.create(request);
// Assert
assertThat(response.getId()).isEqualTo(1L);
assertThat(response.getName()).isEqualTo("An");
// Verify
verify(userRepository).existsByEmail("an@example.com");
verify(userRepository).save(any(User.class));
verify(emailService).sendWelcome(any(User.class));
}
@Test
void shouldThrowWhenEmailExists() {
CreateUserRequest request = new CreateUserRequest("An", "an@example.com", 25);
when(userRepository.existsByEmail("an@example.com")).thenReturn(true);
assertThatThrownBy(() -> userService.create(request))
.isInstanceOf(DuplicateException.class)
.hasMessageContaining("Email already exists");
verify(userRepository, never()).save(any(User.class));
}
@Test
void shouldFindUser() {
User user = new User(1L, "An", "an@example.com", 25);
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
UserResponse response = userService.findById(1L);
assertThat(response.getName()).isEqualTo("An");
}
@Test
void shouldThrowWhenUserNotFound() {
when(userRepository.findById(999L)).thenReturn(Optional.empty());
assertThatThrownBy(() -> userService.findById(999L))
.isInstanceOf(NotFoundException.class);
}
}
11. BDD STYLE
import static org.mockito.BDDMockito.*;
@Test
void shouldCreateUser() {
// Given
given(userRepository.existsByEmail("an@example.com")).willReturn(false);
given(userRepository.save(any(User.class))).willReturn(savedUser);
// When
UserResponse response = userService.create(request);
// Then
assertThat(response.getName()).isEqualTo("An");
then(userRepository).should().save(any(User.class));
}
12. BÀI TẬP THỰC HÀNH
Bài 1: Mock Repository
Test UserService với mock UserRepository.
Bài 2: Verify Behavior
Verify các method được gọi đúng.
Bài 3: ArgumentCaptor
Bắt argument và kiểm tra giá trị.
13. TÓM TẮT
| Khái niệm | Mô tả |
|---|---|
| @Mock | Tạo mock object |
| @InjectMocks | Inject mock vào object |
| @Spy | Wrap object thật |
| when().thenReturn() | Stub return value |
| when().thenThrow() | Stub exception |
| verify() | Verify method được gọi |
| ArgumentCaptor | Bắt argument |
| BDD Style | Given-When-Then |
Bài tiếp theo: 7.3 Integration Test
🧭 Điều Hướng Series
⬅️ Bài trước: BÀI 7.1: UNIT TEST VỚI JUNIT 5
📋 Lộ trình tổng quan: Xem Toàn Bộ Series
➡️ Bài tiếp theo: BÀI 7.3: INTEGRATION TEST
All rights reserved