Mockito
Свайпніть щоб показати меню
У попередньому розділі ми пригадали, як писати юніт-тести. Проте в нашій програмі це не завжди зручно, оскільки тестування контролера вимагає тестування сервісу, а тестування сервісу — тестування репозиторію.
Цей ланцюг залежностей викликає питання: чи потрібно тестувати всі модулі та їхні залежності, чи можна імітувати їхню поведінку — іншими словами, замокати їх?
Mockito: Вступ
Mockito — це популярна бібліотека для Java, яка використовується для створення мок-об'єктів у юніт-тестах.
Вона дозволяє розробникам імітувати поведінку складних зовнішніх залежностей (таких як бази даних, сервіси або API), щоб ізолювати та перевірити логіку окремого компонента (зазвичай класу або методу) без взаємодії з цими зовнішніми системами.
Це робить юніт-тестування більш передбачуваним і швидким, оскільки немає потреби у реальних викликах зовнішніх ресурсів під час тестування.
Мок-об'єкти у Mockito
Це дозволяє розробникам переконатися, що компонент, який тестується (модуль), поводиться коректно без залежності від реальних залежностей, таких як бази даних або сторонні сервіси.
Клас може бути анотований за допомогою @Mock для створення його мок-версії. Ця анотація часто використовується разом із @InjectMocks для автоматичного впровадження залежностей у клас, що тестується.
@Mock
private MyService myServiceMock;
Цей макетний об'єкт може бути ін'єктований як залежність в інший об'єкт за допомогою анотації @InjectMocks. Ця анотація автоматично ін'єктує макетні об'єкти у клас, який тестується.
@InjectMocks
private MyController myController;
Основні методи в Mockito
Mockito надає широкий спектр методів для керування макетними об'єктами, кожен з яких виконує певну функцію у процесі тестування. Нижче наведено найбільш важливі методи разом з їх поясненнями.
Тестування контролерів з Mockito
Тестування контролерів часто передбачає використання моків для сервісів, які викликаються всередині контролера. У цьому контексті Mockito відіграє ключову роль у ізоляції логіки контролера від сервісного шару.
Короткий зміст відео
Ми тестуємо контролери за допомогою класу MockMvc, але що він пропонує і які переваги його використання?
@Autowired
private MockMvc mockMvc;
За допомогою MockMvc можна імітувати різні HTTP-запити (GET, POST, PUT тощо), передавати параметри, заголовки та перевіряти відповіді, коди стану, заголовки й тіла відповідей, що значно спрощує модульне тестування контролерів.
Наприклад, можна використовувати методи на кшталт perform() для виконання запиту, andExpect() для перевірки очікуваних результатів і content() для перевірки вмісту відповіді.
Test.java
Додатково, можна ланцюжити твердження для перевірки HTTP-статусу за допомогою status().isOk(), перевіряти структуру JSON-відповіді через jsonPath() та інше.
Test.java
Ці інструменти забезпечують всебічне тестування поведінки контролера без реального виконання HTTP-запитів.
Ключові анотації
Анотація @WebMvcTest(BookController.class) використовується для тестування BookController, завантажуючи лише ті компоненти, які необхідні для цього конкретного контролера. Вона виключає решту інфраструктури Spring, дозволяючи зосередитися на тестуванні самого контролера.
@WebMvcTest(BookController.class)
public class BookControllerTest
Додатково, анотація @MockBean, застосована до поля bookService, створює мок-версію цього сервісу, що дозволяє імітувати його поведінку. Це допомагає ізолювати контролер від його залежностей та зосередитися на тестуванні його логіки.
@MockBean
private BookService bookService;
Тести для методу updateBook
Ми написали два тести для методу updateBook, які охоплюють усі можливі випадки для цього методу. У першому тесті ми перевіряємо, що все пройшло успішно і що наша сутність була оновлена.
BookControllerTest.java
Цей тест перевіряє успішне оновлення book, якщо він існує в базі даних. Викликається метод updateBook() сервісу з Id книги та об'єктом bookRequestDTO, після чого він повертає об'єкт bookResponseDTO, що представляє оновлену книгу.
Перевірка обробки виключень
Також є тест, у якому виникає виключення в методі updateBook, і необхідно перевірити, як контролер поводиться в такій ситуації.
BookControllerTest.java
Цей тест перевіряє поведінку контролера під час спроби оновити книгу, яка не знайдена у базі даних. Спочатку викликається метод updateBook() сервісу з Id книги та об'єктом bookRequestDTO.
Однак, замість успішного результату, сервіс генерує ApiException, що вказує на те, що книга з вказаним Id не знайдена.
Тестування сервісу з Mockito
Під час тестування сервісу важливо ізолювати його від залежностей, таких як репозиторії або зовнішні API. Mockito дозволяє створювати моки для цих залежностей та визначати їхню поведінку у тестах.
Короткий підсумок відео
Почнемо з тестування нашого методу у випадку, коли він успішно оновлює дані, тобто Id є валідним. Звісно, ми використаємо Mockito для імітації поведінки репозиторію.
BookServiceTest.java
Метод bookRepository.findById("1") імітується для повернення існуючої Book з репозиторію, а bookRepository.save(bookWithRepository) імітується для повернення оновленої книги після збереження змін.
updateBook() метод сервісу викликається для оновлення книги на основі bookRequestDTO, повертаючи BookResponseDTO.
assertNotNull(result) гарантує, що результат не є null, що вказує на успішне оновлення, а assertEquals() перевіряє, що ID, name, author та price оновленої книги відповідають очікуваним значенням.
assertNotNull(result);
assertEquals("1", result.getId());
assertEquals("Updated Name", result.getName());
assertEquals("Updated Author", result.getAuthor());
assertEquals("150", result.getPrice());
Нарешті, verify() переконується, що findById() та save() у репозиторії були викликані з правильними параметрами.
verify(bookRepository).findById("1");
verify(bookRepository).save(bookWithRepository);
Перевірка винятку у сервісі
Цей тест перевіряє, що сервіс викидає ApiException, якщо Book, яку потрібно оновити, не знайдено у базі даних.
BookServiceTest.java
Спочатку створюється об'єкт BookRequestDTO з даними для оновлення, і призначається тестовий ідентифікатор книги Id, який не існує в базі даних — "999". Метод bookRepository.findById(idTest) мокиться для повернення Optional.empty(), що вказує на відсутність книги з таким Id.
when(bookRepository.findById(idTest)).thenReturn(Optional.empty());
Тест використовує метод assertThrows() для перевірки, що при виклику bookService.updateBook(idTest, bookRequestDTO) буде згенеровано ApiException.
Далі методи assertEquals() перевіряють, що повідомлення винятку відповідає очікуваному тексту, а статус помилки дорівнює HttpStatus.NOT_FOUND.
ApiException apiException = assertThrows(ApiException.class, () -> {
bookService.updateBook(idTest, bookRequestDTO);
});
assertEquals("Not found book by id: " + idTest, apiException.getMessage());
assertEquals(HttpStatus.NOT_FOUND, apiException.getHttpStatus());
Метод verify(bookRepository, never()).save(any(Book.class)) гарантує, що метод збереження книги save() не викликався, оскільки книгу не знайдено і оновлення не виконано.
verify(bookRepository, never()).save(any(Book.class));
Підсумок
Mockito — це бібліотека для створення мок-об'єктів, яка дозволяє імітувати поведінку залежностей та ізолювати код, що тестується. Це спрощує тестування, робить його швидшим, стабільнішим і незалежним від зовнішніх ресурсів.
Дякуємо за ваш відгук!
Запитати АІ
Запитати АІ
Запитайте про що завгодно або спробуйте одне із запропонованих запитань, щоб почати наш чат