Какой у вас опыт работы с моками (mock objects) в unit-тестировании?

Ответ

Имею практический опыт использования мок-объектов для изоляции тестируемого класса, преимущественно с фреймворком Mockito.

Типичные сценарии применения:

  1. Создание моков: Аннотации @Mock и @InjectMocks или ручное создание через Mockito.mock().
  2. Задание поведения: Определение ответов мока на вызовы его методов.
    when(userRepository.findById(anyLong()))
        .thenReturn(Optional.of(new User("testUser")));
    when(notificationService.send(any()))
        .thenThrow(new RuntimeException("Service unavailable"));
  3. Верификация взаимодействий: Проверка, что тестируемый код корректно взаимодействует с зависимостями.
    verify(emailService, times(1)).sendWelcomeEmail(userCaptor.capture());
    verify(analyticsClient, never()).trackEvent(any());
  4. Захват аргументов: Использование ArgumentCaptor для проверки передаваемых объектов.

Ключевое понимание: Моки нужны для тестирования логики класса, а не его зависимостей. Избыточное мокирование или мокирование сложных внутренних вызовов — признак плохого дизайна класса (нарушение SRP). В таких случаях предпочитаю рефакторинг, а не использование PowerMock для обхода ограничений (например, мокирования static или final методов).

Ответ 18+ 🔞

А, моки, говоришь? Ну, это ж моя, блядь, ежедневная рутина, как зубы почистить! Сидишь такой, пишешь тесты, а вокруг одни заглушки, как в театре теней, только вместо кукол — объекты, которые нихуя не делают, если им не прикажешь.

Смотри, вот тебе расклад, как я с этим делом вожусь, в основном на Mockito, конечно.

Где я эти штуки применяю, на практике:

  1. Создаю этих актёров-пиздюков. Либо через аннотации @Mock и @InjectMocks — красиво, быстро, либо вручную, через Mockito.mock(), если вдруг настроение мазохистское.
  2. Дрессирую их, блядь. Объясняю каждому, как ему реагировать, когда его вызывают. Вот прям как попугая учишь:
    when(userRepository.findById(anyLong()))
        .thenReturn(Optional.of(new User("testUser"))); // На, держи объект, доволен?
    when(notificationService.send(any()))
        .thenThrow(new RuntimeException("Service unavailable")); // А ты, сука, сломайся тут у меня!
  3. Проверяю, не накосячил ли кто. После того как основной код отработал, иду и сверяюсь: а вызывал ли ты этот сервис? А сколько раз? А может, вообще не должен был?
    verify(emailService, times(1)).sendWelcomeEmail(userCaptor.capture()); // Разок и только разок, я считаю!
    verify(analyticsClient, never()).trackEvent(any()); // А ты, аналитика, сиди и не рыпайся, тебя тут не ждали.
  4. Ловлю аргументы, как шпион. Использую ArgumentCaptor, чтобы поймать, что именно летит в метод зависимого класса. Вдруг там не то, что я ожидал, а какая-нибудь хуйня?

А теперь, главное, что я для себя уяснил, иначе можно с ума сойти, ёпта: моки нужны, чтобы проверить логику твоего класса, а не работу его соседей. Ты же не тестируешь базу данных или внешний API, ты тестируешь свою голову, вложенную в код.

И вот, блядь, главный признак, что ты накосячил с дизайном: если тебе приходится мокать полкласса, включая какие-то внутренние вызовы или, упаси боже, статические методы. Это уже не тест, а цирк с конями! В такие моменты я не бегу за PowerMock, чтобы взломать final-метод, как гопник смартфон. Нет, сука. Я иду на рефакторинг. Потому что если класс превратился в такое чудовище, что его не протестировать — это не проблема моков, это проблема класса. Пора его, блядь, на части рвать, пока он всех не задушил своей сложностью.