skip to content

@DisabledInAotMode

@DisabledInAotMode skips tests whose setup — heavy mocking, runtime context manipulation — cannot work against a frozen context. Knowing it exists shows you have hit the limitation for real.

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

explore

questions

5

Why are tests using @MockitoBean / @MockBean typically incompatible with AOT mode, and how does @DisabledInAotMode help?

level: middleimportance: must knowfreq 45%

answer

  1. mock override = runtime context mutation
  2. AOT context frozen at build time
  3. process-test-aot refresh fails
  4. @MockitoBean/@MockitoSpyBean/@MockBean
  5. skip class + disable at runtime

basics

~20 s

@MockitoBean and @MockBean replace a real bean with a mock by changing the application context. An AOT-built context is frozen and cannot be changed, so the mock cannot be installed. @DisabledInAotMode skips such tests when running in AOT mode.

solid answer

~50 s

Mock-based bean overrides (@MockitoBean, @MockitoSpyBean, or Spring Boot's older @MockBean/@SpyBean) work by mutating the ApplicationContext at test setup: they register or replace a bean definition so autowiring receives the mock. This is done through a context customizer / BeanFactoryPostProcessor that runs while the context is being built. Under AOT, the context is generated and frozen at build time — its bean definitions are fixed and those runtime customizers don't reshape it the same way, so the mock can't be substituted. During build-time test AOT processing, refreshing such a context also tends to fail. @DisabledInAotMode resolves both: Spring skips the class during AOT artifact generation, and at runtime (spring.aot.enabled=true) it disables the test rather than letting it fail or run against the real bean. So you annotate mock-heavy integration tests and keep the rest of the suite AOT-eligible.

code

java · 23 lines
java
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.junit.jupiter.api.Test;
import static org.mockito.BDDMockito.given;
import static org.mockito.ArgumentMatchers.any;

@SpringBootTest
@DisabledInAotMode("@MockitoBean mutates the context; unsupported in a frozen AOT context")
class OrderServiceTests {

    @MockitoBean
    PaymentGateway paymentGateway; // replaces the real bean -> not AOT-compatible

    @org.springframework.beans.factory.annotation.Autowired
    OrderService orderService;

    @Test
    void placesOrder() {
        given(paymentGateway.charge(any())).willReturn(Receipt.ok());
        orderService.place(sampleOrder());
    }
}

go deeper

for a junior

Know that mocking a bean changes the context and a frozen AOT context won't allow it.

for a middle

Explain the runtime-mutation mechanism and both the build-time and runtime failure points.

for a senior

Contrast @MockitoBean override with a @TestConfiguration bean and discuss keeping the suite AOT-eligible.

for a principal

Weigh disabling vs. refactoring against native-image coverage goals across a large test suite.

## Why mocking fights AOT ### How mock-bean overrides work (JVM mode) `@MockitoBean` (Spring Framework 6.2, the framework-level successor to Spring Boot's `@MockBean`) and `@MockitoSpyBean` tell the TestContext framework to **modify the bean factory** while the context is being created: they add a mock/spy bean definition or replace an existing one, so that `@Autowired` targets receive the Mockito object. This is a **runtime mutation of the context** performed by a Spring context customizer. ### Why AOT breaks it AOT (ahead-of-time) processing generates Java code at build time that recreates the context with a **fixed, frozen set of bean definitions** — this is what enables reflection-free startup and GraalVM native images. A frozen context is not meant to be reshaped at runtime, and the AOT-generated bean registration code doesn't re-run the mock-injecting customizers the way a live refresh does. Two failure points result: 1. **Build-time (`process-test-aot`)**: Spring refreshes each unique test context to emit optimized code. Mock-based contexts commonly can't be processed meaningfully and the AOT run errors. 2. **Runtime in AOT mode**: even if generated, the mock substitution wouldn't take effect, so the test could run against the **real** collaborator and give a misleading pass/fail. ### What @DisabledInAotMode does about it - It **excludes** the annotated class from test AOT generation, so the build step doesn't choke on it. - At runtime it **disables** the test when `spring.aot.enabled=true` (checked via `AotDetector.useGeneratedArtifacts()`), so it's reported skipped instead of running incorrectly. ## Practical guidance - Put `@DisabledInAotMode` on mock-driven `@SpringBootTest` / sliced integration tests that you also run through a native/AOT pipeline. - Pure unit tests (plain Mockito, **no** Spring context) are unaffected by AOT and need no annotation — there's no Spring context to freeze. - If you want native coverage of that behavior, refactor to a **@TestConfiguration** that supplies a test double as a normal bean the AOT context can bake in, rather than a runtime override. ## Gotchas - The annotation doesn't 'fix' the mock under AOT — it removes the test from the AOT run. - Overusing it erodes native-image test coverage; treat each use as a deliberate exception.

  • A plain unit test uses Mockito's @Mock with no Spring context. Does it need @DisabledInAotMode?
    No. AOT freezes the Spring ApplicationContext; a test with no Spring context has nothing to freeze, so it is unaffected and needs no annotation.
  • How could you keep native coverage of that behavior instead of disabling the test?
    Replace the runtime @MockitoBean override with a @TestConfiguration that defines the test double as a normal bean. AOT can bake that bean into the generated context, so the test stays AOT-compatible.

saying these in an interview costs you the question

  • Claiming @MockitoBean works fine under AOT because Spring regenerates the context per test
  • Thinking AOT contexts can register new bean definitions at runtime
  • Believing plain-Mockito unit tests (no Spring context) also need @DisabledInAotMode

context

open as a page

What is @DisabledInAotMode and what problem does it solve in Spring tests?

level: juniorimportance: should knowfreq 25%

basics

~10 s

It is a Spring test annotation that skips a test when the app runs in AOT (ahead-of-time) mode. You put it on tests whose setup does not work with an AOT-optimized, pre-built application context.

open as a page

Explain the dual behavior of @DisabledInAotMode (build-time vs runtime) and how Spring detects AOT mode.

level: seniorimportance: should knowfreq 35%

basics

~20 s

It acts at two moments: during build-time test AOT processing it tells Spring to skip generating optimized artifacts for that context, and at runtime it disables the test when AOT mode is active. AOT mode is detected via the spring.aot.enabled system property.

open as a page

Where can @DisabledInAotMode be applied, and what changed across Spring versions?

level: middleimportance: nice to knowfreq 18%

basics

~10 s

It can be placed on a test class or, in newer Spring, on individual test methods. Class-level use came in Spring 6.1; Spring 6.2 added method-level use and an optional reason string.

open as a page

As a tech lead adopting native-image tests, how do you decide between @DisabledInAotMode and refactoring, and what are the coverage trade-offs?

level: principalimportance: nice to knowfreq 15%

basics

~20 s

Disable a test in AOT mode only when its setup truly cannot work in a frozen context (like a mock bean override). Prefer refactoring to a real test-double bean so the test still runs natively. Every @DisabledInAotMode reduces native-image test coverage, so treat it as a deliberate exception, not a default.

open as a page