skip to content

TestExecutionListeners

TestExecutionListeners implement dependency injection, transaction management, SQL scripts and dirtying around each test method, and you can add your own. Knowing they exist explains how @Transactional and @Sql work on a test at all.

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

questions

5

A developer adds @TestExecutionListeners with a single custom listener and suddenly @Autowired stops working in the test. Why, and how does mergeMode fix it?

level: seniorimportance: must knowfreq 45%

answer

  1. default mergeMode = REPLACE_DEFAULTS (the trap)
  2. explicit listeners nuke DI/Tx/Sql/DirtiesContext
  3. MERGE_WITH_DEFAULTS keeps defaults + orders by @Order
  4. null @Autowired => check @TestExecutionListeners
  5. inheritListeners default true

basics

~10 s

Declaring @TestExecutionListeners with explicit listeners REPLACES the defaults by default, so DependencyInjectionTestExecutionListener is gone and injection breaks. Set mergeMode = MERGE_WITH_DEFAULTS to keep the defaults and add yours.

solid answer

~40 s

The @TestExecutionListeners annotation has a mergeMode attribute whose default is MergeMode.REPLACE_DEFAULTS. So the moment you list any listeners explicitly, Spring throws away the auto-registered default set — including DependencyInjectionTestExecutionListener, TransactionalTestExecutionListener, SqlScriptsTestExecutionListener, and DirtiesContextTestExecutionListener. That's why @Autowired, @Transactional, and @Sql all silently stop working. The fix is mergeMode = MergeMode.MERGE_WITH_DEFAULTS, which merges your listeners into the default set. When merging, Spring orders all listeners by their Ordered value / @Order, so your custom listener slots into the correct position rather than always running last. There's also inheritListeners (default true) controlling whether subclasses inherit listeners declared on superclasses. REPLACE_DEFAULTS is only appropriate when you deliberately want a minimal, fully-controlled listener chain.

go deeper

for a junior

Know that mergeMode exists and defaults replace, breaking injection.

for a middle

Choose MERGE_WITH_DEFAULTS to keep DI/Tx/Sql working.

for a senior

Explain ordering during merge and diagnose null @Autowired quickly.

for a principal

Weigh replace vs merge, inheritListeners, and Boot slice interactions.

## The trap `@org.springframework.test.context.TestExecutionListeners` lets you declare which listeners run. Its key attribute: ```java MergeMode mergeMode() default MergeMode.REPLACE_DEFAULTS; ``` The **default is `REPLACE_DEFAULTS`**. This is the single most common gotcha with this annotation: as soon as you write `@TestExecutionListeners(MyListener.class)`, you have *replaced* the entire default chain. The defaults that normally come from `spring.factories` — `DependencyInjectionTestExecutionListener`, `DirtiesContextTestExecutionListener`, `TransactionalTestExecutionListener`, `SqlScriptsTestExecutionListener`, `ServletTestExecutionListener`, event listeners, etc. — are no longer registered. Symptoms: `@Autowired` fields are `null`, `@Transactional` tests actually commit (no rollback listener), `@Sql` scripts silently don't run. ## The fix: MERGE_WITH_DEFAULTS ```java @TestExecutionListeners( listeners = MyCustomTestExecutionListener.class, mergeMode = MergeMode.MERGE_WITH_DEFAULTS ) ``` `MERGE_WITH_DEFAULTS` tells Spring: keep the default listeners and add mine. When merging, Spring does **not** just append; it sorts the combined set by each listener's `getOrder()` / `@Order` value (lower runs earlier in `before*` phases). Duplicate listener types are de-duplicated. So a custom listener with `@Order(2500)` will run *after* `DependencyInjectionTestExecutionListener` (order 2000) but *before* `DirtiesContextTestExecutionListener` (order 3000) — you get precise positioning. With `REPLACE_DEFAULTS`, ordering among the listeners you list is still honoured, but nothing else is present. ## inheritListeners Separately, `inheritListeners()` (default `true`) controls superclass inheritance. If a base test class declares listeners and a subclass declares more with `inheritListeners = true`, the subclass's listeners are appended to the inherited ones (then ordered). Set `false` to shadow the parent's declaration. ## When to use REPLACE_DEFAULTS Rarely. Valid cases: a highly specialised test base that intentionally runs without DI/transaction machinery, or a framework author building a bespoke chain. In application code, almost always prefer `MERGE_WITH_DEFAULTS` so you extend rather than cripple the standard behaviour. ## Boot note Spring Boot's slice annotations (`@DataJpaTest`, `@WebMvcTest`, etc.) and `@SpringBootTest` already merge in Boot-specific listeners (e.g. Mockito reset). If you replace defaults you can also lose those. Prefer merge. ## Diagnosis tip If injection mysteriously breaks after someone adds a listener, immediately check for a `@TestExecutionListeners` without `mergeMode = MERGE_WITH_DEFAULTS`.

  • When you use MERGE_WITH_DEFAULTS, does your listener always run last?
    No — Spring sorts the merged set by Ordered/@Order value, so your listener runs in its ordered position relative to the defaults, not necessarily last.
  • What is a legitimate reason to keep the default REPLACE_DEFAULTS behaviour?
    When you deliberately want a minimal, fully controlled listener chain with none of the standard DI/transaction/SQL machinery — rare, mostly framework-level.
  • How do subclasses interact with listeners declared on a superclass?
    Controlled by inheritListeners (default true), which appends inherited listeners; set false to ignore the parent's declaration.

saying these in an interview costs you the question

  • Believing @TestExecutionListeners adds to the defaults by default
  • Thinking a merged custom listener always executes last
  • Confusing mergeMode with inheritListeners

context

open as a page

What is a TestExecutionListener in the Spring TestContext Framework, and what do the default listeners give you out of the box?

level: juniorimportance: should knowfreq 40%

basics

~20 s

A TestExecutionListener is a plugin that Spring calls around your tests (e.g. before/after each test method). Default listeners auto-provide dependency injection, @Transactional rollback, @Sql script execution, and @DirtiesContext handling — you get them for free.

open as a page

Walk through the TestExecutionListener callback lifecycle. What is the difference between prepareTestInstance, beforeTestMethod, and beforeTestExecution, and when does each fire relative to @BeforeEach?

level: middleimportance: should knowfreq 35%

basics

~20 s

prepareTestInstance fires once right after the test object is created (injection happens here). beforeTestMethod fires before each test, before @BeforeEach. beforeTestExecution fires after @BeforeEach, just before the test body runs. after* callbacks mirror them in reverse.

open as a page

How does Spring decide the order in which the default listeners (DependencyInjection, DirtiesContext, Transactional, Sql) run, and why does that order matter?

level: seniorimportance: should knowfreq 25%

basics

~20 s

Each listener has an order value (via Ordered/@Order); Spring sorts by it. Lower value runs earlier in before* callbacks. Roughly: DependencyInjection (2000) < DirtiesContext (3000) < Transactional (4000) < Sql (5000). Order matters so injection and transactions are set up before SQL runs.

open as a page

Design a custom TestExecutionListener that seeds and cleans up per-test data. Which callbacks do you implement, how do you register it without breaking defaults, and what are the pitfalls?

level: principalimportance: should knowfreq 20%

basics

~10 s

Implement TestExecutionListener, override beforeTestMethod to seed and afterTestMethod to clean up, give it @Order after the transactional listener, and register it via @TestExecutionListeners(listeners=..., mergeMode=MERGE_WITH_DEFAULTS) so defaults keep working.

open as a page