Java-фреймворк для mock-тестирования Mockito
Стандартные библиотеки классов Java
Что такое mock-тестирование?
В разработке программного обеспечения модульное тестирование является важным средством проверки функциональности кода. Однако когда наш код зависит от внешних систем (таких как базы данных, веб-сервисы и т. д.), прямое тестирование может столкнуться с несколькими проблемами:
- Внешние зависимости могут быть нестабильными или недоступными
- Скорость выполнения тестов становится ниже
- Сложно смоделировать различные граничные условия
Mock-тестирование (имитационное тестирование) решает эти проблемы, создавая «заменители» для объектов. Mock-объекты могут:
- Имитировать поведение реальных объектов
- Проверять, происходят ли взаимодействия ожидаемым образом
- Не выполнять фактическую логику реальных объектов
Почему выбрать Mockito?
Mockito в настоящее время является самым популярным фреймворком для Mock-тестирования в экосистеме Java и обладает следующими преимуществами:
- Лаконичный API: плавная кривая обучения, простой старт
- Мощная функциональность: поддерживает проверку вызовов методов, настройку возвращаемых значений, выбрасывание исключений и т. д.
- Хорошая читаемость: тестовый код интуитивно понятен и прост для понимания
- Активное сообщество: постоянно обновляется и поддерживается, имеет исчерпывающую документацию
Основные концепции Mockito
1. Создание Mock-объектов
Пример
List mockedList = Mockito.mock(List.class);
// Способ 2: использовать аннотации (требуется MockitoJUnitRunner)
@Mock
List mockedList;
2. Настройка поведения методов
Пример
when(mockedList.size()).thenReturn(100);
// Вернуть "first", когда вызывается mockedList.get(0)
when(mockedList.get(0)).thenReturn("first");
// Выбросить исключение, когда вызывается mockedList.get(1)
when(mockedList.get(1)).thenThrow(new RuntimeException());
3. Проверка взаимодействий
Пример
verify(mockedList).add("one");
// Проверить, что mockedList.clear() никогда не вызывался
verify(mockedList, never()).clear();
// Проверить, что mockedList.add("two") вызывался как минимум дважды
verify(mockedList, atLeast(2)).add("two");
Практические примеры применения Mockito
Тестирование сервиса пользователей
Предположим, у нас естьUserServiceзависит отUserRepository:
Пример
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("Пользователь не найден"));
}
}
Используйте Mockito для тестирования:
Пример
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testGetUserById_Success() {
// Подготовка тестовых данных
User mockUser = new User(1L, "testUser");
// Настроить поведение мока
when(userRepository.findById(1L))
.thenReturn(Optional.of(mockUser));
// Выполнить тест
User result = userService.getUserById(1L);
// Проверить результат
assertEquals("testUser", result.getUsername());
verify(userRepository).findById(1L);
}
@Test(expected = UserNotFoundException.class)
public void testGetUserById_NotFound() {
when(userRepository.findById(2L))
.thenReturn(Optional.empty());
userService.getUserById(2L);
}
}
Продвинутые возможности Mockito
1. Матчеры аргументов
Пример
when(mockedList.get(anyInt())).thenReturn("element");
// Аргумент определённого типа
when(mockedList.contains(anyString())).thenReturn(истина);
// Пользовательский матчер
when(mockedList.add(argThat(arg -> arg.length() > 5))).thenReturn(истина);
2. Проверка порядка вызовов
Пример
inOrder.verify(mockedList).add("first");
inOrder.verify(mockedList).add("second");
3. Частичный мок (Spy)
Пример
List spyList = spy(realList);
// Вызвать реальный метод
spyList.add("real");
// Замокировать конкретный метод
doReturn(100).when(spyList).size();
Лучшие практики
- Не злоупотребляйте моками: Создавайте моки только для необходимых зависимостей, делайте тесты реалистичными
- Проверяйте взаимодействия в меру: Сосредоточьтесь на важных взаимодействиях, не проверяйте каждый вызов метода
- Делайте тесты краткими: каждый тестовый метод должен проверять только одну функциональную точку
- Разумно организуйте тестовый код: используйте @Before для общей настройки
- Сочетайте с другими инструментами тестирования: используйте вместе с JUnit, AssertJ и т. д.
Часто задаваемые вопросы (FAQ)
Вопрос: В чём разница между Mockito и PowerMock?
Ответ: Mockito в основном используется для мокирования обычных объектов, тогда как PowerMock может мокировать статические методы, конструкторы и т. д. Однако PowerMock ухудшает изоляцию тестов, поэтому рекомендуется сначала использовать Mockito.
Вопрос: Когда следует использовать Spy вместо Mock?
Ответ: Используйте Spy, когда вам нужно в основном реальное поведение и нужно изменить лишь несколько методов. Если вам нужен полный контроль над поведением объекта, используйте Mock.
Вопрос: Можно ли использовать Mockito для final-классов или методов?
Ответ: Начиная с Mockito 2.1.0, final-классы и методы можно мокировать через конфигурацию, но вам нужно добавитьmockito-inlineзависимость.