Spy objects help you watch how real objects behave during tests. They let you check what methods were called and with what data, without changing the real object's behavior.
Spy objects in JUnit
MyClass realObject = new MyClass();
MyClass spyObject = Mockito.spy(realObject);
// Use spyObject in your test
Mockito.verify(spyObject).methodName(expectedArgument);Spy wraps a real object, so the original methods run unless stubbed.
Use Mockito.verify() to check method calls on the spy.
List<String> list = new ArrayList<>(); List<String> spyList = Mockito.spy(list); spyList.add("apple"); Mockito.verify(spyList).add("apple");
getData() method to return a mocked result, while still tracking calls.MyService service = new MyService();
MyService spyService = Mockito.spy(service);
Mockito.doReturn("mocked result").when(spyService).getData();
String result = spyService.getData();
Mockito.verify(spyService).getData();This test creates a spy of a real list. It adds two fruits, then verifies the add method was called with each fruit. It also checks the list size is 2, showing the real methods ran.
import static org.mockito.Mockito.*; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; public class SpyTest { @Test public void testSpyObject() { List<String> realList = new ArrayList<>(); List<String> spyList = spy(realList); spyList.add("banana"); spyList.add("orange"); verify(spyList).add("banana"); verify(spyList).add("orange"); assert(spyList.size() == 2); } }
Spies call real methods by default, so be careful with side effects.
You can stub spy methods to change behavior using doReturn() or when().
Use spies when you want to partially mock an object but keep most behavior real.
Spy objects let you watch real objects during tests without changing their behavior by default.
They help verify method calls and arguments while keeping original logic intact.
Use spies to combine real behavior with verification in your unit tests.