JavaRush /Курси /Модуль 5. Spring /Лекція 135: Тестування сервісів і репозиторіїв

Лекція 135: Тестування сервісів і репозиторіїв

Модуль 5. Spring
Рівень 9 , Лекція 4
Відкрита

Сервіси — це ті частини твого коду, де міститься бізнес-логіка. Якщо контролери — це фасад додатку, то сервіси — це свого роду "мізки". Помилки в сервісах можуть призвести до некоректного виконання бізнес-процесів.

Тестування сервісів допомагає:

  • Перевірити, що логіка додатку працює так, як задумано.
  • Переконатися, що всі виклики методів і взаємодії із залежностями виконуються коректно.
  • Виявити баги ще до збірки й деплоя додатку.

Mockito як твій найкращий друг

Для тестування сервісів ми активно використовуватимемо Mockito. Чому? Тому що нам треба протестувати саме бізнес-логіку, ізолювавши її від усіх зовнішніх залежностей, таких як бази даних або зовнішні API.

Основні кроки тестування сервісів

  1. Створити моки для всіх залежностей сервісу (репозиторії, інші сервіси тощо).
  2. Налаштувати поведінку моків, використовуючи when() і thenReturn().
  3. Перевірити, що методи сервісу викликають очікувані дії і повертають коректні результати.
  4. Опційно переконатися, що залежності викликаються потрібну кількість разів (або не викликаються взагалі), використовуючи методи типу verify().

Практика: тестування сервісів

Візьмемо приклад. У нас є сервіс, який рахує знижки для користувачів. Він звертається до репозиторію, щоб отримати інформацію про користувача, і використовує іншу логіку додатку.

Сервіс


@Service
public class DiscountService {

    private final UserRepository userRepository;

    public DiscountService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public double calculateDiscount(Long userId) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("User not found"));

        if (user.isPremium()) {
            return 20.0; // 20% знижка для преміум-користувачів
        }
        return 5.0; // 5% знижка для всіх інших
    }
}

Тестування

1. Створимо клас тесту і налаштуємо моки:


@ExtendWith(MockitoExtension.class)
class DiscountServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private DiscountService discountService;

    // Тестові дані
    private final User premiumUser = new User(1L, "PremiumUser", true);
    private final User regularUser = new User(2L, "RegularUser", false);

    // ...
}

2. Тестуємо метод calculateDiscount() для преміум-користувачів:


@Test
void shouldReturn20PercentDiscountForPremiumUsers() {
    // Налаштовуємо мок
    when(userRepository.findById(1L)).thenReturn(Optional.of(premiumUser));

    // Дія
    double discount = discountService.calculateDiscount(1L);

    // Перевірка
    assertEquals(20.0, discount);
    verify(userRepository, times(1)).findById(1L); // Переконаємось, що репозиторій викликався рівно 1 раз
}

3. Тестуємо метод calculateDiscount() для звичайних користувачів:


@Test
void shouldReturn5PercentDiscountForRegularUsers() {
    // Налаштовуємо мок
    when(userRepository.findById(2L)).thenReturn(Optional.of(regularUser));

    // Дія
    double discount = discountService.calculateDiscount(2L);

    // Перевірка
    assertEquals(5.0, discount);
    verify(userRepository, times(1)).findById(2L);
}

4. Обробляємо випадок, коли користувача не знайдено:


@Test
void shouldThrowExceptionWhenUserNotFound() {
    // Налаштовуємо мок
    when(userRepository.findById(anyLong())).thenReturn(Optional.empty());

    // Перевірка на виключення
    Exception exception = assertThrows(IllegalArgumentException.class,
        () -> discountService.calculateDiscount(99L));

    assertEquals("User not found", exception.getMessage());
    verify(userRepository, times(1)).findById(99L);
}

Тепер ми впевнені, що наш сервіс працює коректно й реагує на всі можливі сценарії.


Вступ до тестування репозиторіїв

Репозиторії — це міст між твоїм додатком і базою даних. Тому важливо переконатися, що вони правильно взаємодіють з базою даних, виконуючи коректні запити та обробки.

Як тестувати репозиторії?

  1. Використовувати вбудовану базу даних, наприклад, H2 (в пам'яті) для тестів.
  2. Анотація @DataJpaTest допомагає автоматично налаштувати тестове середовище для роботи з JPA репозиторіями.
  3. Перевіряти, що результати запитів відповідають очікуванням.

Практика: Тестування репозиторіїв

Сутність


@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private boolean isPremium;

    // Конструктори, геттери та сеттери
}

Репозиторій


@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findAllByIsPremium(boolean isPremium);
}

Тестування

1. Створимо тестовий клас:


@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private UserRepository userRepository;

    // ...
}

2. Тестуємо метод findAllByIsPremium:


@Test
void shouldFindAllPremiumUsers() {
    // Підготовка тестових даних
    User premiumUser = new User(null, "PremiumUser", true);
    User regularUser = new User(null, "RegularUser", false);

    entityManager.persist(premiumUser);
    entityManager.persist(regularUser);
    entityManager.flush();

    // Дія
    List<User> result = userRepository.findAllByIsPremium(true);

    // Перевірка
    assertEquals(1, result.size());
    assertEquals("PremiumUser", result.get(0).getName());
}

3. Тестуємо стандартні методи JPA (наприклад, findById):


@Test
void shouldFindUserById() {
    // Підготовка даних
    User user = new User(null, "TestUser", false);
    User savedUser = entityManager.persistFlushFind(user);

    // Перевірка
    Optional<User> result = userRepository.findById(savedUser.getId());
    assertTrue(result.isPresent());
    assertEquals("TestUser", result.get().getName());
}

Підсумки

Тестування сервісів з використанням Mockito ізолює логіку від зовнішніх залежностей, дозволяючи зосередитись лише на перевірці бізнес-логіки. У той час тестування репозиторіїв з анотацією @DataJpaTest і базою H2 допомагає переконатися в коректності запитів і роботи з даними.

Ти успішно підготував свою кодову базу до реальних задач, і баги більше не зможуть ховатися в твоєму додатку! Тепер можна рухатись далі до більш складних сценаріїв тестування, про які ти дізнаєшся в наступних лекціях.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ