Ответ
@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() — они сначала настраивают ловушку, а потом уже метод вызывается. Не промахнись, а то сам себя высечешь.