@Spy lets you test real objects but still control or check some parts of their behavior. It helps when you want to test most of the real code but replace or watch a few methods.
@Spy for partial mocking in JUnit
@Spy private ClassName objectName;
Use @Spy on a field to create a partial mock of that object.
You can use Mockito.when() to override specific methods.
@Spy private List<String> spyList = new ArrayList<>();
@Spy private MyService service;
Mockito.when(spyList.size()).thenReturn(100);This test creates a spy on a real ArrayList. It adds two items, then overrides the size() method to return 100. The get(0) method still returns the real value. The test also verifies that add("one") was called.
import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(MockitoExtension.class) public class SpyExampleTest { @Spy private List<String> spyList = new ArrayList<>(); @Test public void testSpyPartialMocking() { spyList.add("one"); spyList.add("two"); // Override size method when(spyList.size()).thenReturn(100); // Real methods work assertEquals("one", spyList.get(0)); // Overridden method returns mocked value assertEquals(100, spyList.size()); // Verify method call verify(spyList).add("one"); } }
Use @Spy when you want to keep most real behavior but control some parts.
Remember to initialize spies properly, for example with MockitoExtension in JUnit 5.
Be careful: overriding too many methods can make tests confusing.
@Spy creates a partial mock of a real object.
You can override or verify specific methods while keeping others real.
It helps test complex objects without full mocking.
Practice
@Spy annotation do in JUnit testing?Solution
Step 1: Understand @Spy purpose
@Spy creates a real instance of the class but allows selective method overriding.Step 2: Compare with other mocking types
Unlike full mocks, spies call real methods unless stubbed.Final Answer:
Creates a real object but allows overriding some methods for partial mocking. -> Option CQuick Check:
@Spy = Partial real object with override [OK]
- Confusing @Spy with @Mock which creates full fake objects
- Thinking @Spy mocks static methods
- Assuming @Spy runs tests automatically
Solution
Step 1: Recognize proper spy initialization
Using @Spy requires calling MockitoAnnotations.openMocks(this) to initialize the spy fields.Step 2: Check other options
@Spy MyClass mySpy = new MyClass(); misses initialization call; C uses mock instead of spy; D tries spying on null which is invalid.Final Answer:
@Spy MyClass mySpy; MockitoAnnotations.openMocks(this); -> Option AQuick Check:
Initialize spies with openMocks(this) [OK]
- Not calling MockitoAnnotations.openMocks(this)
- Confusing spy creation with mock creation
- Trying to spy on null or uninitialized objects
@Spy
List<String> spyList = new ArrayList<>();
@Test
void testSpyList() {
spyList.add("one");
spyList.add("two");
Mockito.doReturn(100).when(spyList).size();
System.out.println(spyList.size());
}Solution
Step 1: Understand spy behavior with doReturn
The spy calls real methods unless stubbed. Here, size() is stubbed to return 100.Step 2: Analyze output of spyList.size()
Despite adding two elements, size() returns 100 due to stubbing.Final Answer:
100 -> Option BQuick Check:
Stubbed spy method returns stub value [OK]
- Expecting real method size() to return actual list size
- Using when(spyList.size()) instead of doReturn for spies
- Forgetting spy initialization causing NullPointerException
@Spy
MyService service;
@Test
void testService() {
service = new MyService();
Mockito.doReturn("mocked").when(service).getData();
assertEquals("mocked", service.getData());
}Solution
Step 1: Check spy initialization
The @Spy field must be initialized by calling MockitoAnnotations.openMocks(this) before use.Step 2: Analyze assignment and usage
Assigning new instance manually is allowed but without initialization, spy won't work properly.Final Answer:
The spy object is not initialized with MockitoAnnotations.openMocks(this). -> Option AQuick Check:
Always init @Spy with openMocks(this) [OK]
- Forgetting to call MockitoAnnotations.openMocks(this)
- Thinking manual assignment replaces initialization
- Believing doReturn can't be used with spies
PaymentProcessor to test only the processPayment() method while keeping other methods real. Which approach using @Spy is best to achieve this?Solution
Step 1: Understand partial mocking goal
Partial mocking means real methods run except those stubbed.Step 2: Choose spy with selective stubbing
Using @Spy and stubbing only processPayment() lets you test that method while others behave normally.Step 3: Evaluate other options
Mocking entire class loses real behavior; overriding all except one is complex; no spy means no partial mocking.Final Answer:
Create a spy of PaymentProcessor and stub only processPayment() method with Mockito.doReturn(). -> Option DQuick Check:
@Spy + stub target method = partial mocking [OK]
- Mocking entire class losing real method calls
- Overriding too many methods manually
- Not using @Spy for partial mocking
Ready to prove it?
Pass the test and this pattern moves unknown
