junior
Как тестировать сервисный слой с помощью MockitoExtension?
Для юнит-тестирования сервисного слоя не нужен контекст Spring. Используется @ExtendWith(MockitoExtension.class) совместно с @Mock и @InjectMocks.
@Mock— создаёт мок-объект для зависимости.@InjectMocks— создаёт экземпляр тестируемого класса и внедряет в него все моки.
Полный пример теста сервиса
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@Mock
private PasswordEncoder passwordEncoder;
@InjectMocks
private UserServiceImpl userService;
@Test
void shouldCreateUserSuccessfully() {
// Arrange
UserDto dto = new UserDto("Иван", "ivan@example.com", "password");
when(passwordEncoder.encode("password")).thenReturn("encodedPassword");
when(userRepository.save(any(User.class))).thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(1L);
return user;
});
// Act
User result = userService.create(dto);
// Assert
assertNotNull(result.getId());
assertEquals("Иван", result.getName());
assertEquals("encodedPassword", result.getPassword());
verify(userRepository).save(any(User.class));
verify(emailService).sendWelcomeEmail("ivan@example.com");
}
@Test
void shouldThrowExceptionWhenUserNotFound() {
when(userRepository.findById(999L)).thenReturn(Optional.empty());
assertThrows(UserNotFoundException.class,
() -> userService.findById(999L));
verify(userRepository).findById(999L);
verifyNoInteractions(emailService);
}
@Test
void shouldUpdateUserName() {
User existing = new User(1L, "Иван", "ivan@example.com");
when(userRepository.findById(1L)).thenReturn(Optional.of(existing));
when(userRepository.save(any(User.class))).thenReturn(existing);
User result = userService.updateName(1L, "Пётр");
assertEquals("Пётр", result.getName());
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(userRepository).save(captor.capture());
assertEquals("Пётр", captor.getValue().getName());
}
}
Преимущества подхода
- Быстрота — не поднимается контекст Spring
- Изоляция — тестируется только логика сервиса
- Детерминированность — нет зависимости от базы данных, сети и т.д.
На собеседовании: интервьюер хочет убедиться, что вы понимаете разницу между
@MockBean(Spring-контекст) и@Mock+@InjectMocks(чистый Mockito). Частая ошибка — использовать@SpringBootTestдля юнит-тестов сервисов вместоMockitoExtension.