skip to content

As a tech lead, when would you avoid @MockitoBean in favour of another testing approach, and how do you keep a large Spring suite fast?

level: principalimportance: nice to knowfreq 14%

answer

  1. logic → plain @Mock, no context (ms-fast)
  2. layer → slice test (@WebMvcTest / @DataJpaTest)
  3. whole app → @SpringBootTest sparingly
  4. share override set via base class → reuse context
  5. many mocks = design smell; avoid @DirtiesContext

basics

~20 s

Avoid @MockitoBean when a pure unit test (plain @Mock, no context) or a narrow slice test would do — it forces a full context and can fragment the context cache. Reserve it for integration tests that genuinely need real Spring wiring plus one faked collaborator.

solid answer

~50 s

@MockitoBean is the right tool only when you need the real ApplicationContext wiring but must isolate a specific collaborator. It is the wrong tool for testing a single class's logic — there, construct the class directly with plain Mockito @Mock collaborators and skip Spring entirely; those tests run in milliseconds. Because each distinct override set becomes part of the context cache key, scattering ad-hoc @MockitoBean fields fragments the cache into many contexts, ballooning suite time and memory past the default 32-context limit. My strategy: default to pure unit tests for logic; use slice tests (@WebMvcTest with @MockitoBean for the service layer, @DataJpaTest) where you need a thin real context; reserve full @SpringBootTest + @MockitoBean for a small number of end-to-end integration tests; and where mocks are needed, standardise the override set via shared base classes so contexts are reused. I also avoid combining @MockitoBean with @DirtiesContext except when unavoidable, since that evicts and rebuilds contexts.

code

java · 19 lines
java
// Prefer this pure unit test for single-class logic — no Spring context at all.
@ExtendWith(MockitoExtension.class)
class OrderCalculatorTest {
    @Mock DiscountPolicy discountPolicy;

    @Test
    void appliesDiscount() {
        when(discountPolicy.rateFor(any())).thenReturn(0.10);
        var calc = new OrderCalculator(discountPolicy); // constructed directly
        assertThat(calc.total(order(100))).isEqualByComparingTo("90.00");
    }
}

// Use @MockitoBean in a SLICE test, not a full context, when you need a real layer.
@WebMvcTest(OrderController.class)
class OrderControllerTest {
    @Autowired MockMvc mvc;
    @MockitoBean OrderService orderService; // fill the un-loaded service layer
}

go deeper

for a junior

Know that a plain unit test with @Mock is faster than loading a Spring context.

for a middle

Distinguish unit vs slice vs full-context tests and where @MockitoBean fits.

for a senior

Justify the choice by context-cache cost and pick slices to keep the real surface thin.

for a principal

Set team-wide conventions (base classes, test-pyramid policy, cache tuning) that bound context count and treat many-mocks as a design smell.

## The decision framework `@MockitoBean` sits between two cheaper alternatives. Choosing well is a leadership/architecture concern because it dominates suite runtime. ### 1. Pure unit test (no Spring) — the default for logic If you are testing the behaviour of **one class**, you almost never need Spring. Construct it directly and pass **plain `@Mock`** (or `Mockito.mock(...)`) collaborators via the constructor. No context loads; tests run in **milliseconds**. Use this for domain/service logic, validation, mapping, branching. Reaching for `@MockitoBean` here needlessly loads a context and fragments the cache. ### 2. Slice tests — thin real context When you need *some* real Spring infrastructure but not the whole app: - `@WebMvcTest` loads only the web layer (controllers, `MockMvc`, converters) and you supply the service layer with `@MockitoBean`. - `@DataJpaTest` loads JPA/repositories against an embedded DB. These contexts are far smaller and cheaper than `@SpringBootTest`, and `@MockitoBean` is the idiomatic way to fill in the beans the slice doesn't load. ### 3. Full @SpringBootTest + @MockitoBean — sparingly Reserve the **whole-application** context for a **small** number of true integration tests where the interaction across many real beans is the thing under test, and you only fake the truly external edges (payment, email, third-party HTTP). Every such class with a distinct mock set is a distinct cached context. ## Keeping a large suite fast - **Bound the context count.** Distinct `@MockitoBean` sets → distinct `MergedContextConfiguration` keys → distinct contexts. Standardise the override set with a **shared abstract base test class** so many tests reuse **one** context. Stay under the default `ContextCache` limit of **32** to avoid LRU eviction/rebuild thrash. - **Push mocking down.** Prefer unit/slice tests; treat full-context tests as a scarce resource. - **Avoid @DirtiesContext with mocks** unless required — it evicts the context so the next test rebuilds it. Mock reset (default `MockReset.AFTER`) already isolates methods **without** rebuilding, so you rarely need `@DirtiesContext` just to clean up a mock. - **Measure.** Enable `org.springframework.test.context.cache` DEBUG logging to watch cache hits/misses/size; investigate low hit ratios. - **Watch phantom mocks.** In refactor-heavy code, enable `enforceOverride = true` so a renamed bean fails the test rather than silently creating a new mock. ## Architectural signal If you find yourself needing many `@MockitoBean`s to make a test tractable, that is often a **design smell**: the class under test has too many collaborators or the context is too coarse. The fix is usually better module boundaries and smaller, more testable units — not more mocks. ## Team conventions worth setting - 'Unit test by default; slice when you need a layer; full context only for integration.' - 'Share the mock set through a base class; don't invent one-off override combinations.' - 'No `@DirtiesContext` without a written reason.' These keep both correctness (isolation) and speed (context reuse) under control at scale.

  • A teammate uses @SpringBootTest + @MockitoBean to test a single service method's branching. What do you suggest?
    Rewrite it as a pure unit test: construct the service directly with plain @Mock collaborators and drop the Spring context entirely. It runs orders of magnitude faster and avoids adding a context-cache entry. Reserve @SpringBootTest for genuine multi-bean integration behaviour.
  • When is @DirtiesContext genuinely needed alongside mocks?
    Rarely — mock reset (MockReset.AFTER) already isolates test methods without a rebuild. You need @DirtiesContext only when a test mutates non-mock context state that cannot be reset (e.g., a stateful singleton or modified bean definitions), accepting the cost of a context rebuild.

saying these in an interview costs you the question

  • Defaulting to @SpringBootTest + @MockitoBean for every test, including single-class logic.
  • Adding @DirtiesContext to 'clean up' mocks that Spring already resets automatically.
  • Ignoring that varied override sets multiply cached contexts and slow the suite.

context