Resetting, Clearing, and Reusing Mocks in Mockito

Illustration for Resetting, Clearing, and Reusing Mocks in Mockito
By Last updated:

Mockito simplifies testing by allowing us to mock dependencies. However, in larger test suites, managing mock lifecycles becomes important. Sometimes you need to reset a mock, clear its interactions, or reuse it across multiple test cases. Done correctly, this keeps your tests clean, efficient, and reliable.

This tutorial covers resetting, clearing, and reusing mocks in Mockito, their differences, use cases, and best practices.


Why Manage Mock Lifecycles?

  • Prevent State Leakage: Ensure one test’s behavior doesn’t affect another.
  • Improve Test Readability: Keep tests focused by removing unnecessary noise.
  • Support Reusability: Reduce boilerplate by reusing mocks.
  • Ensure Stability in CI/CD: Avoid flaky tests caused by mock leftovers.

Think of mocks like whiteboards in a classroom: sometimes you erase interactions, sometimes you reset the whole board, and sometimes you keep the board but write over it.


Resetting Mocks with reset()

reset(mock) removes all stubbing and interaction history.

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;

class ResetMocksTest {

    @Test
    void shouldResetMock() {
        List<String> mockList = mock(List.class);

        when(mockList.size()).thenReturn(5);
        System.out.println(mockList.size()); // prints 5

        reset(mockList);

        System.out.println(mockList.size()); // now returns 0 (default)
    }
}
  • Use when you want to reuse the same mock with completely fresh behavior.
  • All stubs and verifications are cleared.

⚠️ Best Practice: Avoid overusing reset() — it can make tests harder to read.


Clearing Interactions with clearInvocations()

clearInvocations(mock) removes only the interaction history, not stubs.

@Test
void shouldClearInvocations() {
    List<String> mockList = mock(List.class);

    mockList.add("Hello");
    verify(mockList).add("Hello");

    clearInvocations(mockList);

    // Still stubbed methods remain, but interactions reset
    verify(mockList, never()).add("Hello");
}
  • Keeps stubbing intact.
  • Useful when verifying the same mock in multiple steps.

Resetting vs Clearing

Operation Removes Stubbing Removes Interactions Use Case
reset(mock) ✅ Yes ✅ Yes Start completely fresh
clearInvocations() ❌ No ✅ Yes Retain stubbing, clear history

Reusing Mocks Across Tests

Instead of creating new mocks for every test, you can reuse mocks with JUnit 5 lifecycle annotations.

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockitoAnnotations;
import static org.mockito.Mockito.*;

class ReuseMocksTest {

    List<String> mockList;

    @BeforeEach
    void setUp() {
        mockList = mock(List.class);
    }

    @Test
    void testFirst() {
        when(mockList.size()).thenReturn(10);
        assert mockList.size() == 10;
    }

    @Test
    void testSecond() {
        when(mockList.isEmpty()).thenReturn(true);
        assert mockList.isEmpty();
    }
}

This ensures each test gets a fresh instance, avoiding test pollution.


Best Practices

  • Prefer new mocks per test (cleaner and less error-prone).
  • Use clearInvocations() for multiple verifications in a single test.
  • Use reset() only when absolutely necessary.
  • Don’t reset mocks in shared test fixtures — it leads to brittle tests.
  • Document why a reset/clear is needed for clarity.

Mockito + Testcontainers

Mockito manages mock dependencies, while Testcontainers ensures real dependencies are tested. Use them together for hybrid testing:

import org.testcontainers.containers.PostgreSQLContainer;

@Test
void shouldStartDatabase() {
    try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
        postgres.start();
        assert postgres.isRunning();
    }
}

Version Tracker

  • JUnit 4 → JUnit 5: Cleaner lifecycle management via @BeforeEach and extensions.
  • Mockito Updates: Enhanced APIs for clearInvocations() and better reset control.
  • Testcontainers Growth: Parallel test execution with isolated containers.

Conclusion & Key Takeaways

Managing mock lifecycles is critical for reliable, maintainable tests. Mockito provides flexible tools like reset() and clearInvocations() to control mock state.

Key Takeaways:

  • Use reset() to start fresh.
  • Use clearInvocations() to keep stubbing but reset history.
  • Prefer creating new mocks per test for simplicity.
  • Combine Mockito with Testcontainers for realistic pipelines.

FAQ

1. What’s the difference between reset() and clearInvocations()?
reset() clears everything, while clearInvocations() only clears interaction history.

2. Should I reset mocks after every test?
No, create new mocks instead — it’s cleaner and safer.

3. Can I clear stubbing without resetting?
No, stubbing can only be removed by reset().

4. Does reset() affect spies?
Yes, it resets spies to their original state.

5. Can I reuse mocks across multiple test classes?
Yes, but initialize fresh ones per class or use dependency injection.

6. Is reset() a code smell?
Often yes — prefer fresh mocks per test unless you have a strong reason.

7. Can I reset multiple mocks at once?
Yes, reset(mock1, mock2, ...) works.

8. Do resets slow down tests?
Minimal impact, but overuse complicates tests.

9. Should I reset mocks in CI/CD pipelines?
No, use test isolation instead.

10. How does Testcontainers complement Mockito?
Mockito isolates dependencies; Testcontainers validates real external systems.