skip to content

@TestBean

@TestBean overrides a bean with a static factory method in the test class, giving you a hand-written fake without Mockito. A good answer when a stub needs real behaviour rather than recorded stubbing.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is @TestBean in Spring 6.2 and how do you use it to override a bean in a test?

level: juniorimportance: should knowfreq 22%

answer

  1. Spring 6.2 bean override
  2. static <field>TestOverride() factory
  3. real fake, not a Mockito mock
  4. field type selects the bean
  5. instance field + static no-arg method

basics

~10 s

@TestBean (Spring 6.2) replaces a bean in the test's application context with an instance you build in a static factory method inside the test class. By convention the method is named <fieldName>TestOverride.

solid answer

~40 s

@TestBean is a Spring 6.2 bean-override annotation. You put it on an instance field of your test class; Spring finds the matching context bean (by the field's type) and swaps in an object you create yourself. That object comes from a static, no-argument factory method whose name, by convention, is the field name plus TestOverride (e.g. field pricingClient -> method pricingClientTestOverride). Unlike @MockitoBean, no Mockito mock is involved — you return a hand-written fake or stub, so it needs no Mockito dependency and behaves deterministically. It's part of the Spring TestContext framework, so it works under @SpringBootTest / SpringExtension like the rest of the test infrastructure. Use it when you want a real, controlled fixture (a fake gateway, a fixed Clock) rather than a mock you configure with when/verify.

code

java · 19 lines
java
@SpringBootTest
class OrderServiceTests {

    @TestBean
    private PricingClient pricingClient; // overrides the context's PricingClient bean

    // Convention: <fieldName>TestOverride, static, no args, compatible return type.
    static PricingClient pricingClientTestOverride() {
        return amount -> BigDecimal.TEN; // deterministic hand-written fake
    }

    @Autowired
    OrderService orderService; // receives the fake via the shared context

    @Test
    void usesFakePricing() {
        assertThat(orderService.quote()).isEqualByComparingTo("10");
    }
}

go deeper

for a junior

Know it swaps a context bean for a fixture built in a static <field>TestOverride() method.

for a middle

Know it's non-Mockito, by-type by default, and the method-name convention plus the static/no-arg rules.

for a senior

Contrast with @MockitoBean/@MockitoSpyBean and know when a hand-written fake beats a mock.

for a principal

Frame it as replacing @Primary/test-@Configuration hacks and understand context-cache impact.

## What it is `@TestBean` (introduced in **Spring Framework 6.2**, part of `org.springframework.test.context.bean.override`) is a *bean override* annotation. It lets a test replace a bean in the `ApplicationContext` with an instance the test itself supplies via a **static factory method**. It is the non-Mockito sibling of `@MockitoBean` and `@MockitoSpyBean`. ## The two pieces 1. **The annotated field** — a non-static instance field in the test class, e.g. `@TestBean PricingClient pricingClient;`. The field's **type** is used (by default) to locate the bean to override. 2. **The static factory method** — produces the replacement instance. By **convention** its name is the field name + `TestOverride` (field `pricingClient` -> method `pricingClientTestOverride`). It must be `static`, take **no arguments**, and return a type assignable to the bean/field type. ## How it works During context setup the TestContext framework registers a `BeanOverride` that intercepts the target bean definition and substitutes the factory-produced instance. The overridden bean is then injected wherever it is `@Autowired`, including into other beans in the context — not just the test field. So the whole context sees your fake. ## Why use it instead of a mock - You want a **real, hand-written fake/stub** (deterministic behaviour, no `when(...).thenReturn(...)` ceremony). - You want to avoid a Mockito dependency or Mockito's proxying limitations (e.g. final classes, records, sealed types). - Good for value-like collaborators: a fixed `Clock`, an in-memory repository, a canned HTTP gateway. ## Key terms - **Bean override**: replacing a context bean at test time without editing production `@Configuration`. - **Factory method**: the static method that constructs the fixture. - **By-type selection**: default matching strategy using the field type. ## Gotchas - Field must be an **instance** field; the factory method must be **static** and **no-arg**. - If the convention-named method is missing you get a startup error — the name must match exactly. - Overriding by type fails if the type is ambiguous (multiple candidates) — then you must add a `name` or a qualifier.

  • What happens if you name the factory method something other than <fieldName>TestOverride?
    Spring can't resolve it by convention and context startup fails; you must either rename it to the convention or set the methodName attribute explicitly.
  • Does the override affect only the test field or the whole context?
    The whole context — the bean definition is replaced, so every other bean that autowires that type receives your fake too.

context

open as a page

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

level: middleimportance: should knowfreq 28%

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.

open as a page

What are the rules for the static factory method behind @TestBean, and how do you point to a non-conventional or external method?

level: seniorimportance: should knowfreq 20%

basics

~20 s

The factory method must be static, take no arguments, and return a type assignable to the bean. By default it's named <fieldName>TestOverride and lives in the test class; use methodName to choose a different name or an external class via "fqcn#method".

open as a page

How does @TestBean select which bean to override, and what does the enforceOverride attribute control?

level: seniorimportance: nice to knowfreq 15%

basics

~20 s

By default @TestBean matches by the field's type. If several beans match, disambiguate with the name attribute (bean name) or a qualifier. enforceOverride (default false) controls whether a missing target is an error: false creates a bean if none exists, true fails when there's nothing to override.

open as a page

What architectural considerations (context caching, replacing older override hacks) come with using @TestBean across a suite?

level: principalimportance: nice to knowfreq 12%

basics

~20 s

Bean overrides are part of the test context cache key, so each distinct set of @TestBean overrides yields a separate cached ApplicationContext. Overusing varied overrides fragments the cache and slows the suite. @TestBean also replaces older hacks like test @Configuration + @Primary or context customizers.

open as a page