Bird
Raised Fist0
JUnittesting~5 mins

@Spy for partial mocking in JUnit

Choose your learning style10 modes available
Introduction

@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.

You want to test a real object but mock only some methods.
You want to verify if certain methods were called on a real object.
You want to keep the original behavior but override specific parts.
You want to avoid writing a full mock for a complex class.
You want to combine real logic with controlled test behavior.
Syntax
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.

Examples
This creates a spy on a real ArrayList object.
JUnit
@Spy
private List<String> spyList = new ArrayList<>();
This creates a spy on MyService. You can override some methods if needed.
JUnit
@Spy
private MyService service;
This overrides the size() method to return 100, even though spyList is a real list.
JUnit
Mockito.when(spyList.size()).thenReturn(100);
Sample Program

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.

JUnit
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");
    }
}
OutputSuccess
Important Notes

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.

Summary

@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

(1/5)
1. What does the @Spy annotation do in JUnit testing?
easy
A. Automatically runs all test methods in a class.
B. Creates a completely fake object with no real method calls.
C. Creates a real object but allows overriding some methods for partial mocking.
D. Mocks static methods of a class.

Solution

  1. Step 1: Understand @Spy purpose

    @Spy creates a real instance of the class but allows selective method overriding.
  2. Step 2: Compare with other mocking types

    Unlike full mocks, spies call real methods unless stubbed.
  3. Final Answer:

    Creates a real object but allows overriding some methods for partial mocking. -> Option C
  4. Quick Check:

    @Spy = Partial real object with override [OK]
Hint: Remember: @Spy means real object with some fake methods [OK]
Common Mistakes:
  • Confusing @Spy with @Mock which creates full fake objects
  • Thinking @Spy mocks static methods
  • Assuming @Spy runs tests automatically
2. Which of the following is the correct way to initialize a spy object in a JUnit 5 test class using Mockito?
easy
A. @Spy MyClass mySpy; MockitoAnnotations.openMocks(this);
B. @Spy MyClass mySpy = new MyClass();
C. @Spy MyClass mySpy = Mockito.mock(MyClass.class);
D. @Spy MyClass mySpy = Mockito.spy(null);

Solution

  1. Step 1: Recognize proper spy initialization

    Using @Spy requires calling MockitoAnnotations.openMocks(this) to initialize the spy fields.
  2. 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.
  3. Final Answer:

    @Spy MyClass mySpy; MockitoAnnotations.openMocks(this); -> Option A
  4. Quick Check:

    Initialize spies with openMocks(this) [OK]
Hint: Always call MockitoAnnotations.openMocks(this) to init @Spy [OK]
Common Mistakes:
  • Not calling MockitoAnnotations.openMocks(this)
  • Confusing spy creation with mock creation
  • Trying to spy on null or uninitialized objects
3. Given the following code snippet, what will be the output of the test?
@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());
}
medium
A. 2
B. 100
C. 0
D. Throws NullPointerException

Solution

  1. Step 1: Understand spy behavior with doReturn

    The spy calls real methods unless stubbed. Here, size() is stubbed to return 100.
  2. Step 2: Analyze output of spyList.size()

    Despite adding two elements, size() returns 100 due to stubbing.
  3. Final Answer:

    100 -> Option B
  4. Quick Check:

    Stubbed spy method returns stub value [OK]
Hint: Stub spy methods with doReturn to override real calls [OK]
Common Mistakes:
  • Expecting real method size() to return actual list size
  • Using when(spyList.size()) instead of doReturn for spies
  • Forgetting spy initialization causing NullPointerException
4. Identify the error in the following test code using @Spy:
@Spy
MyService service;

@Test
void testService() {
    service = new MyService();
    Mockito.doReturn("mocked").when(service).getData();
    assertEquals("mocked", service.getData());
}
medium
A. The spy object is not initialized with MockitoAnnotations.openMocks(this).
B. You cannot assign a new instance to a @Spy annotated field.
C. doReturn cannot be used with spy objects.
D. The assertEquals method is used incorrectly.

Solution

  1. Step 1: Check spy initialization

    The @Spy field must be initialized by calling MockitoAnnotations.openMocks(this) before use.
  2. Step 2: Analyze assignment and usage

    Assigning new instance manually is allowed but without initialization, spy won't work properly.
  3. Final Answer:

    The spy object is not initialized with MockitoAnnotations.openMocks(this). -> Option A
  4. Quick Check:

    Always init @Spy with openMocks(this) [OK]
Hint: Always initialize @Spy fields before test runs [OK]
Common Mistakes:
  • Forgetting to call MockitoAnnotations.openMocks(this)
  • Thinking manual assignment replaces initialization
  • Believing doReturn can't be used with spies
5. You want to partially mock a complex service class PaymentProcessor to test only the processPayment() method while keeping other methods real. Which approach using @Spy is best to achieve this?
hard
A. Create a new PaymentProcessor instance without @Spy and call processPayment() directly.
B. Mock the entire PaymentProcessor class with @Mock and implement processPayment() manually.
C. Use @Spy but override all methods except processPayment() to avoid side effects.
D. Create a spy of PaymentProcessor and stub only processPayment() method with Mockito.doReturn().

Solution

  1. Step 1: Understand partial mocking goal

    Partial mocking means real methods run except those stubbed.
  2. Step 2: Choose spy with selective stubbing

    Using @Spy and stubbing only processPayment() lets you test that method while others behave normally.
  3. Step 3: Evaluate other options

    Mocking entire class loses real behavior; overriding all except one is complex; no spy means no partial mocking.
  4. Final Answer:

    Create a spy of PaymentProcessor and stub only processPayment() method with Mockito.doReturn(). -> Option D
  5. Quick Check:

    @Spy + stub target method = partial mocking [OK]
Hint: Spy real object, stub only target method for partial mocking [OK]
Common Mistakes:
  • Mocking entire class losing real method calls
  • Overriding too many methods manually
  • Not using @Spy for partial mocking
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard