skip to content

When would you choose @TestBean over @MockitoBean (or @MockitoSpyBean)?

level: middleimportance: should knowfreq 28%

answer

  1. TestBean = real fake
  2. MockitoBean = mock + verify
  3. MockitoSpyBean = wrap real bean
  4. fake vs stubbing readability
  5. no Mockito / final-class friendly

basics

~20 s

Use @TestBean when you want a real hand-written fake/stub with deterministic behaviour and no Mockito. Use @MockitoBean for a Mockito mock you configure with when/verify, and @MockitoSpyBean to wrap a real bean and spy on it.

solid answer

~50 s

All three are Spring 6.2 bean-override annotations, but they differ in what they inject. @TestBean injects an instance you construct in a static factory method — a real fake or stub, no Mockito involved, so no when/verify and no Mockito proxy limits (works cleanly with final classes, records, sealed types). @MockitoBean injects a Mockito mock: nothing real runs unless you stub it, and you can verify interactions. @MockitoSpyBean wraps the actual bean so real methods run unless overridden. Choose @TestBean when behaviour matters and you want a deterministic collaborator — a fixed Clock, an in-memory repository, a canned gateway — especially when hand-written fakes read better than a pile of stubbings, or when you want to avoid Mockito entirely. Choose @MockitoBean when you need interaction verification or per-test stubbing; @MockitoSpyBean when you want the real bean plus selective overrides.

code

java · 21 lines
java
@SpringBootTest
class CheckoutTests {

    // Real deterministic fixture — no stubbing needed.
    @TestBean
    Clock clock;
    static Clock clockTestOverride() {
        return Clock.fixed(Instant.parse("2026-01-01T00:00:00Z"), ZoneOffset.UTC);
    }

    // Mockito mock — configured/verified per test.
    @MockitoBean
    PaymentGateway paymentGateway;

    @Test
    void chargesOnce() {
        when(paymentGateway.charge(any())).thenReturn(Result.ok());
        // ... exercise ...
        verify(paymentGateway, times(1)).charge(any());
    }
}

go deeper

for a junior

Know @TestBean gives a real fake while @MockitoBean gives a mock.

for a middle

Articulate the decision guide and that @TestBean avoids Mockito and its proxy limits.

for a senior

Discuss readability/maintainability trade-offs and final-class/record cases.

for a principal

Set team conventions on when fakes vs mocks are preferred and why.

## The three Spring 6.2 override annotations Spring 6.2 added a unified bean-override facility with three concrete annotations: - **`@TestBean`** — replace a bean with an instance from a **static factory method** (a real fake/stub). No Mockito. - **`@MockitoBean`** — replace a bean with a **Mockito mock**. Methods return default values until stubbed with `when(...).thenReturn(...)`; you can `verify(...)` interactions. - **`@MockitoSpyBean`** — **wrap** the real bean in a Mockito spy: real methods run unless explicitly stubbed; you can still verify. (These supersede Spring Boot's older `@MockBean`/`@SpyBean`, deprecated in Boot 3.4.) ## Decision guide Pick **@TestBean** when: - You want a **deterministic, real implementation** — e.g. `Clock.fixed(...)`, an in-memory `Map`-backed repository, a stub HTTP client returning canned responses. - The behaviour is complex enough that a hand-written fake is **clearer** than many `when(...)` lines. - You want to **avoid Mockito** or the collaborator is a **final class / record / sealed type** that Mockito can't easily proxy (inline mock-maker helps but a fake sidesteps it entirely). - You don't need interaction verification. Pick **@MockitoBean** when you need **interaction verification** (`verify`), **per-test stubbing**, argument matchers/captors, or to fully isolate a collaborator with zero real logic. Pick **@MockitoSpyBean** when you want the **real bean's behaviour** but need to override or observe one or two methods. ## Trade-offs - **@TestBean** couples the test to a concrete fake you must maintain; but it's fast, deterministic, and mock-framework-free. - **@MockitoBean/@MockitoSpyBean** are quick to set up per test and support verification, but over-stubbing produces brittle, hard-to-read tests. ## Shared mechanics All three select the target bean **by type** by default (with `name`/qualifier disambiguation), participate in the **test context cache key**, and inject into the whole context — the differences are purely in *what* replaces the bean.

  • Can you verify interactions on a @TestBean the way you would on a @MockitoBean?
    Not through Spring — @TestBean injects a plain object with no Mockito recording. If you need verification you must build it into the fake yourself (e.g. a counter) or use @MockitoBean instead.
  • What replaced Spring Boot's @MockBean and @SpyBean?
    @MockitoBean and @MockitoSpyBean (Spring Framework 6.2); Boot deprecated @MockBean/@SpyBean in 3.4.

saying these in an interview costs you the question

  • Claiming @TestBean creates a Mockito mock you can stub with when(...)
  • Thinking @TestBean supports verify(...) out of the box
  • Saying @TestBean and @MockitoSpyBean are the same (spy wraps the real bean; TestBean replaces it with your instance)

context