Что такое аннотация @Spy в Mockito и для чего она используется?

Ответ

@Spy — это аннотация фреймворка Mockito для создания частичного мока (spy) реального объекта.

Принцип работы:

  • Создается обертка вокруг реального экземпляра класса.
  • По умолчанию все методы вызываются у реального объекта.
  • Вы можете замокать (stub) конкретные методы spy-объекта, чтобы изменить их поведение для теста, оставив остальные методы работающими как есть.

Зачем использовать:

  • Когда нужно протестировать взаимодействие с реальным объектом, но подменить один или несколько его методов.
  • Полезно для тестирования legacy-кода, где сложно выделить зависимости для полного мока.

Пример:

@Spy
private List<String> spyList = new ArrayList<>();

@Test
public void testSpy() {
    // Реальный метод работает
    spyList.add("real");
    // Замокали один метод
    doReturn(100).when(spyList).size();

    assertEquals("real", spyList.get(0)); // Вызов реального метода
    assertEquals(100, spyList.size());     // Вызов замоканного метода
}

Важно: Для мока void-методов или методов, где нужно выбросить исключение, используйте doReturn(), doThrow(), doAnswer(), а не when(...).thenReturn() на spy-объекте, чтобы избежать вызова реального метода.

Ответ 18+ 🔞

А, ну вот, смотри, сейчас я тебе про эту штуку расскажу, а то народ путается, как последние идиоты. @Spy, блядь.

Представь себе, что у тебя есть реальный, живой объект. Не какая-то там заглушка, а настоящий, со всеми потрохами. Так вот, @Spy — это как взять этого честного работягу и надеть на него наручники, блядь. Не чтобы посадить, а чтобы за ним присматривать и, если что, дать по рукам.

Как это, сука, работает:

  • Берёшь реальный экземпляр — вот он, стоит, работает.
  • Обматываешь его плёнкой Mockito, получается такой полуреальный полупризрачный шпион.
  • По умолчанию он всё делает как честный пацан — все методы вызываются у оригинального объекта.
  • Но! Если какой-то метод начинает выёбываться или тебе нужно, чтобы он в рамках теста вёл себя по-другому — ты можешь его подменить (stub), оставив остальных в покое.

Зачем это, нахуй, нужно?

  • Когда тебе нужно потестить код, который уже работает с реальной хернёй, но ты хочешь подменить в ней один гвоздь, а не перестраивать весь дом.
  • Идеально для этого старого говнокода (legacy, блядь), где всё так переплетено, что хуй поймёшь, где зависимость, а где уже геморрой. Тут шпион — твой лучший друг, ёпта.

Смотри, как это выглядит на практике:

@Spy
private List<String> spyList = new ArrayList<>(); // Настоящий список, но под колпаком

@Test
public void testSpy() {
    // Реальный метод работает, как и положено
    spyList.add("real");
    // А вот этот метод мы подменили нахрен
    doReturn(100).when(spyList).size(); // Говорим: "Размер у тебя теперь, сука, сто, запомни!"

    assertEquals("real", spyList.get(0)); // Тут вызывается настоящий метод — всё ок
    assertEquals(100, spyList.size());    // А тут уже наша подстава сработала — 100!
}

Вот тут, блядь, важный момент, чтобы не обосраться: Когда мокаешь методы у шпиона, особенно void-методы или те, где надо исключение кинуть, НЕ ИСПОЛЬЗУЙ when(...).thenReturn(...) сразу. Потому что эта конструкция сначала вызовет реальный метод, а потом уже подменит, и можно получить неожиданный пиздец. Вместо этого бери doReturn(), doThrow(), doAnswer() — они сначала настраивают ловушку, а потом уже метод вызывается. Не промахнись, а то сам себя высечешь.