0
0
JUnittesting~15 mins

@Spy for partial mocking in JUnit - Build an Automation Script

Choose your learning style9 modes available
Verify partial mocking using @Spy annotation in JUnit
Preconditions (2)
Step 1: Create a test class CalculatorTest
Step 2: Annotate a Calculator instance with @Spy
Step 3: Stub the multiply method to return a fixed value (e.g., 100) when called with any arguments
Step 4: Call the add method with inputs 5 and 3
Step 5: Call the multiply method with inputs 5 and 3
Step 6: Verify that add method returns the actual sum (8)
Step 7: Verify that multiply method returns the stubbed value (100)
✅ Expected Result: The add method returns 8 (real method execution), and multiply method returns 100 (stubbed). The test passes confirming partial mocking with @Spy.
Automation Requirements - JUnit 5 with Mockito
Assertions Needed:
Assert that add(5,3) returns 8
Assert that multiply(5,3) returns 100
Best Practices:
Use @ExtendWith(MockitoExtension.class) to enable Mockito annotations
Use @Spy annotation on the Calculator instance
Use Mockito.when() to stub multiply method
Use assertions from org.junit.jupiter.api.Assertions
Avoid stubbing methods unnecessarily
Use descriptive test method names
Automated Solution
JUnit
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class CalculatorTest {

    static class Calculator {
        public int add(int a, int b) {
            return a + b;
        }

        public int multiply(int a, int b) {
            return a * b;
        }
    }

    @Spy
    Calculator calculator;

    @Test
    void testPartialMockingWithSpy() {
        // Stub multiply method to always return 100
        when(calculator.multiply(anyInt(), anyInt())).thenReturn(100);

        // Call add method - real method should be called
        int sum = calculator.add(5, 3);

        // Call multiply method - stubbed method should be called
        int product = calculator.multiply(5, 3);

        // Verify results
        assertEquals(8, sum, "Add method should return real sum");
        assertEquals(100, product, "Multiply method should return stubbed value");
    }
}

This test class uses @ExtendWith(MockitoExtension.class) to enable Mockito annotations.

The Calculator class has two methods: add and multiply.

We annotate a Calculator instance with @Spy to create a partial mock. This means real methods are called unless stubbed.

We stub the multiply method to always return 100 regardless of input using when(...).thenReturn(...).

When calling add(5,3), the real method runs and returns 8.

When calling multiply(5,3), the stubbed method runs and returns 100.

Assertions verify these expected results, confirming partial mocking works as intended.

Common Mistakes - 4 Pitfalls
{'mistake': 'Using @Mock instead of @Spy for partial mocking', 'why_bad': "Mocks replace all methods with stubs by default, so real methods won't run unless explicitly told.", 'correct_approach': 'Use @Spy to create a partial mock where real methods run unless stubbed.'}
{'mistake': 'Not using @ExtendWith(MockitoExtension.class) with Mockito annotations', 'why_bad': "Mockito annotations like @Spy won't be initialized, causing NullPointerException.", 'correct_approach': 'Add @ExtendWith(MockitoExtension.class) on the test class to enable annotation processing.'}
{'mistake': 'Stubbing methods incorrectly with wrong argument matchers', 'why_bad': "If argument matchers don't match actual call, stub won't apply and real method runs unexpectedly.", 'correct_approach': 'Use correct argument matchers like anyInt() to match all inputs when stubbing.'}
Calling real methods on a @Mock instance expecting partial behavior
Bonus Challenge

Now add data-driven testing with 3 different input pairs for add and multiply methods using @Spy

Show Hint