Bird
Raised Fist0
JUnittesting~8 mins

Spy objects in JUnit - Framework Patterns

Choose your learning style10 modes available
Framework Mode - Spy objects
Folder Structure for JUnit Test Framework with Spy Objects
src/
├── main/
│   └── java/
│       └── com/example/app/
│           └── service/
│               └── UserService.java
└── test/
    └── java/
        └── com/example/app/
            ├── service/
            │   └── UserServiceTest.java  <-- Tests using spy objects here
            └── utils/
                └── TestUtils.java
Test Framework Layers
  • Test Classes: Contain JUnit test methods. Use spy objects to monitor real objects' behavior.
  • Spy Objects: Partial mocks that wrap real objects to track method calls and arguments.
  • Service Layer: Application logic classes under test (e.g., UserService).
  • Utilities: Helper methods for test setup, common assertions, or spy creation.
  • Configuration: Settings for test environment, dependencies, and mocking framework setup.
Configuration Patterns
  • Dependency Injection: Inject real objects into spies to wrap them easily.
  • Mockito Setup: Use @ExtendWith(MockitoExtension.class) and @Spy annotations for spy creation.
  • Environment Profiles: Use Maven or Gradle profiles to run tests in different environments if needed.
  • Test Properties: Store test-specific configs (e.g., test database URLs) in src/test/resources.
Test Reporting and CI/CD Integration
  • JUnit Reports: Use built-in JUnit XML reports for test results.
  • Mockito Verification: Spy objects allow verification of method calls, which appear as assertion results.
  • CI/CD Integration: Integrate with Jenkins, GitHub Actions, or GitLab CI to run tests automatically on code push.
  • Fail Fast: Tests fail immediately if spy verification fails, providing clear feedback.
Best Practices for Using Spy Objects in JUnit
  1. Use Spies Only When Needed: Prefer mocks or real objects unless you need to track real method calls.
  2. Keep Tests Clear: Verify only relevant interactions to avoid brittle tests.
  3. Use Annotations: Use @Spy and @InjectMocks for clean setup.
  4. Reset Spies: Reset spy state between tests to avoid cross-test interference.
  5. Combine with Real Logic: Spies allow partial mocking, so real methods run unless stubbed.
Self Check Question

Where in this folder structure would you add a new spy object for a PaymentService class to monitor its method calls during tests?

Key Result
Use spy objects in JUnit tests to monitor real object behavior while running actual methods.

Practice

(1/5)
1. What is the main purpose of using a spy object in JUnit testing?
easy
A. To generate random data for testing
B. To completely replace the real object with a fake one
C. To observe real object behavior and verify method calls
D. To speed up test execution by skipping method calls

Solution

  1. Step 1: Understand spy object behavior

    A spy wraps a real object, allowing it to behave normally while tracking interactions.
  2. Step 2: Identify spy's main use

    Spies let you verify if certain methods were called without changing the real behavior.
  3. Final Answer:

    To observe real object behavior and verify method calls -> Option C
  4. Quick Check:

    Spy = observe real + verify calls [OK]
Hint: Spy watches real object calls without changing behavior [OK]
Common Mistakes:
  • Confusing spy with mock that replaces behavior
  • Thinking spy speeds up tests by skipping calls
  • Believing spy generates test data
2. Which of the following is the correct way to create a spy object using Mockito in JUnit?
easy
A. MyClass spy = Mockito.spy(new MyClass());
B. MyClass spy = Mockito.mock(MyClass.class);
C. MyClass spy = new MyClass();
D. MyClass spy = Mockito.verify(new MyClass());

Solution

  1. Step 1: Recall Mockito spy syntax

    Mockito creates a spy by wrapping a real instance: Mockito.spy(realObject).
  2. Step 2: Match options to syntax

    MyClass spy = Mockito.spy(new MyClass()); correctly uses Mockito.spy(new MyClass()) to create a spy.
  3. Final Answer:

    MyClass spy = Mockito.spy(new MyClass()); -> Option A
  4. Quick Check:

    Spy creation = Mockito.spy(real instance) [OK]
Hint: Spy wraps real instance with Mockito.spy() [OK]
Common Mistakes:
  • Using Mockito.mock() instead of spy()
  • Trying to spy without a real instance
  • Confusing verify() with spy()
3. Given the code below, what will be the output of the test?
List<String> list = new ArrayList<>();
List<String> spyList = Mockito.spy(list);
spyList.add("apple");
spyList.add("banana");
Mockito.verify(spyList).add("apple");
System.out.println(spyList.size());
medium
A. 0
B. Throws an exception
C. 1
D. 2

Solution

  1. Step 1: Understand spy behavior on add()

    Spy calls real methods, so add("apple") and add("banana") add elements to the list.
  2. Step 2: Verify method call and size

    Verification confirms add("apple") was called. The list now contains two elements, so size() returns 2.
  3. Final Answer:

    2 -> Option D
  4. Quick Check:

    Spy calls real methods, size = 2 [OK]
Hint: Spy calls real methods; size reflects added elements [OK]
Common Mistakes:
  • Assuming spy does not call real methods
  • Expecting size to be 0 after add calls
  • Confusing verify with changing behavior
4. Identify the error in the following test code using a spy:
List<String> list = new ArrayList<>();
List<String> spyList = Mockito.spy(list);
Mockito.when(spyList.size()).thenReturn(5);
spyList.add("orange");
System.out.println(spyList.size());
medium
A. Spy must be created with Mockito.mock()
B. Cannot stub spy methods with Mockito.when() directly
C. List cannot be spied because it's an interface
D. No error; output will be 1

Solution

  1. Step 1: Recognize stubbing on spy

    Stubbing spy methods with Mockito.when() can cause real method calls and errors.
  2. Step 2: Correct stubbing approach

    Use doReturn(5).when(spyList).size() to stub spy methods safely.
  3. Final Answer:

    Cannot stub spy methods with Mockito.when() directly -> Option B
  4. Quick Check:

    Spy stubbing = use doReturn() not when() [OK]
Hint: Stub spy methods with doReturn(), not when() [OK]
Common Mistakes:
  • Using Mockito.when() on spy causing real calls
  • Confusing spy creation with mock creation
  • Thinking interfaces can't be spied
5. You want to test a service method that calls a repository's save() method. You want to keep the real repository behavior but verify save() was called exactly once. Which is the best approach using spy objects?
hard
A. Create a spy of the repository, call the service method, then verify save() was called once
B. Mock the repository and stub save() to do nothing, then verify call count
C. Use a real repository without spy or mock and check database manually
D. Create a mock repository and verify save() was never called

Solution

  1. Step 1: Understand spy use case

    Spy lets real methods run while tracking calls, perfect for verifying save() without changing behavior.
  2. Step 2: Apply spy to repository and verify

    Create spy of repository, call service method, then verify save() was called once.
  3. Final Answer:

    Create a spy of the repository, call the service method, then verify save() was called once -> Option A
  4. Quick Check:

    Spy = real behavior + verify calls [OK]
Hint: Spy real repo to verify calls without changing behavior [OK]
Common Mistakes:
  • Mocking instead of spying loses real behavior
  • Checking database manually is slow and unreliable
  • Verifying save was never called contradicts test goal
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