skip to content

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%

answer

  1. static, no-arg, assignable return
  2. <field>TestOverride convention
  3. methodName for rename
  4. fqcn#method for external
  5. exactly one match or startup fails

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".

solid answer

~40 s

@TestBean's replacement instance comes from a static, no-argument factory method whose return type is assignable to the overridden bean's type. By convention Spring looks for a method named <fieldName>TestOverride in the test class (searching the test class hierarchy and enclosing classes). If you want a different name, set the methodName attribute. To use a method in another class, set methodName to a fully-qualified reference "com.acme.Fakes#clock". Exactly one candidate method must resolve — zero or multiple matches cause a context-startup failure, so it's fail-fast. The method runs during context initialization to produce the fixture, which then replaces the bean everywhere it's injected. Note the method takes no parameters: it can't receive the original bean, so if you need to decorate the real instance you'd reach for @MockitoSpyBean instead.

code

java · 21 lines
java
@SpringBootTest
class ReportTests {

    // Non-convention local method name via methodName:
    @TestBean(methodName = "buildClock")
    Clock clock;
    static Clock buildClock() {
        return Clock.fixed(Instant.EPOCH, ZoneOffset.UTC);
    }

    // Reuse a shared factory in another class via fqcn#method:
    @TestBean(methodName = "com.acme.test.Fakes#inMemoryRepo")
    ReportRepository repository;
}

// Shared factory class
public final class Fakes {
    public static ReportRepository inMemoryRepo() {
        return new InMemoryReportRepository();
    }
}

go deeper

for a junior

Remember static, no-arg, and the TestOverride naming convention.

for a middle

Know methodName overrides the name and supports external fqcn#method references.

for a senior

Explain fail-fast single-match resolution and the no-arg (can't wrap) limitation vs @MockitoSpyBean.

for a principal

Design shared factory classes for reusable fakes and standardise their placement.

## Factory method contract For `@TestBean`, the replacement object is produced by a **static factory method** with these rules: - **`static`** — it's invoked without a test instance. - **No arguments** — it cannot receive the original bean or any dependency; it must construct the fixture itself. - **Return type assignable to the target** — the returned type must be compatible with the bean/field type. ## Method resolution 1. **Convention (default):** Spring looks for `<fieldName>TestOverride`. For `@TestBean Clock clock;` it seeks `static Clock clockTestOverride()`. The search covers the **test class and its superclasses/enclosing classes**. 2. **`methodName` attribute (same class):** `@TestBean(methodName = "buildClock")` uses a differently named local static method — handy when one method backs multiple fields or the convention name is awkward. 3. **`methodName` as an external reference:** `@TestBean(methodName = "com.acme.test.Fakes#clock")` points to a static method in **another class**, letting you centralise reusable fakes in a shared factory class. ## Fail-fast resolution **Exactly one** matching method must be found. Zero matches (typo, wrong signature) or an ambiguous set cause the `ApplicationContext` to **fail to start** with a clear error — you learn immediately, not via a mysterious NPE. ## When the factory runs The method executes during context setup, before the test body, so the fixture is present for every autowiring point. ## Important limitation Because the method is **no-arg**, it **cannot access the original bean**. There is no built-in way to wrap/decorate the real instance with `@TestBean`. If you need the real bean's behaviour plus overrides, use **`@MockitoSpyBean`** (a spy wraps the real bean). ## Gotchas - Non-static or parameterised method -> resolution failure. - Convention name mismatch -> failure unless `methodName` is set. - External reference must use the `fqcn#method` syntax exactly. - One shared factory can back several `@TestBean` fields via `methodName`.

  • Can the factory method receive the original bean so you can wrap it?
    No — @TestBean's factory is no-arg and cannot see the original bean. For wrap/decorate semantics use @MockitoSpyBean, which spies on the real bean.
  • What if two @TestBean fields need the same fake?
    Point both at one static factory via methodName (local name or fqcn#method); each field still resolves its own bean by type/name, but they can share the constructing method.

saying these in an interview costs you the question

  • Believing the factory method can take the original bean as a parameter
  • Making the method an instance (non-static) method
  • Assuming a differently-named method is picked up automatically without methodName

context