Close

Spring Core Testing - Understanding TestExecutionListener

[Last Updated: Feb 12, 2026]

A TestExecutionListener listens for the test execution events published by the TestContextManager.

Default Test ExecutionListeners

The spring-test module declares all default TestExecutionListeners in its META-INF/spring.factories properties file.

Spring.factories (spring-test version 4.3.10.RELEASE):

# Default TestExecutionListeners for the Spring TestContext Framework
#
org.springframework.test.context.TestExecutionListener = \
	org.springframework.test.context.web.ServletTestExecutionListener,\
	org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener,\
	org.springframework.test.context.support.DependencyInjectionTestExecutionListener,\
	org.springframework.test.context.support.DirtiesContextTestExecutionListener,\
	org.springframework.test.context.transaction.TransactionalTestExecutionListener,\
	org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener
 .......

All above listeners have a specific responsibility in Spring integration tests execution. For example, DependencyInjectionTestExecutionListener provides support for dependency injection and initialization of test instances.

What is spring.factories?

This is a general purpose factory loading mechanism provided by Spring core. It requires the file spring.factories (a properties file) located under META-INF.

SpringFactoriesLoader.loadFactories(MyService.class, ..) method can be used to load a specified class. This method returns list of specified class instances. There might be multiple spring.factories files in different JAR files in the class path which may defined a particular type multiple times.

Note that spring-boot also uses this mechanism to load auto configurations, check out here.

The TestExecutionListener interface

Definition of TestExecutionListener

Version: 7.0.4
 package org.springframework.test.context;
 public interface TestExecutionListener {
     default void beforeTestClass(TestContext testContext) 1
                                  throws Exception;
     default void prepareTestInstance(TestContext testContext) 2
                                      throws Exception;
     default void beforeTestMethod(TestContext testContext) 3
                                   throws Exception;
     default void beforeTestExecution(TestContext testContext) 4
                                      throws Exception;
     default void afterTestExecution(TestContext testContext) 5
                                     throws Exception;
     default void afterTestMethod(TestContext testContext) 6
                                  throws Exception;
     default void afterTestClass(TestContext testContext) 7
                                 throws Exception;
 }
1Pre-processes a test class before execution of all tests within the class. (Since 3.0)
2Prepares the Object of the supplied TestContext — for example, to inject dependencies.
3Pre-processes a test before execution of before lifecycle callbacks of the underlying test framework — for example, by setting up test fixtures.
4Pre-processes a test immediately before execution of the java.lang.reflect.Method in the supplied TestContext — for example, for timing or logging purposes. (Since 5.0)
5Post-processes a test immediately after execution of the java.lang.reflect.Method in the supplied TestContext — for example, for timing or logging purposes. (Since 5.0)
6Post-processes a test after execution of after lifecycle callbacks of the underlying test framework — for example, by tearing down test fixtures.
7Post-processes a test class after execution of all tests within the class. (Since 3.0)

As seen above, a TestExecutionListener implementation can receive events during different test execution stages. Let's see what we can do with TestContext which is passed to the listener on each event.

The TextContext interface

TestContext contains information of spring context and of the target test class/methods as well, which can be used to alter the tests behavior or extend their functionality.

Example of Custom TestExecutionListener

Let's see how to defined our own TestExecutionListener to understand how it works.

Creating a simple Spring application

package com.logicbig.example;

import org.springframework.stereotype.Component;

@Component
public class MyBean {
    public void doSomething() {
        System.out.println("-- in MyBean.doSomething() method --");
    }
}
package com.logicbig.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@ComponentScan
public class AppConfig {
}

Writing a TestExecutionListener

package com.logicbig.example;

import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;

public class MyListener implements TestExecutionListener {
    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
        System.out.println("MyListener.beforeTestClass()");
    }

    @Override
    public void prepareTestInstance(TestContext testContext) throws Exception {
        System.out.println("MyListener.prepareTestInstance()");

    }

    @Override
    public void beforeTestMethod(TestContext testContext) throws Exception {
        System.out.println("MyListener.beforeTestMethod()");
    }

    @Override
    public void afterTestMethod(TestContext testContext) throws Exception {
        System.out.println("MyListener.afterTestMethod()");
    }

    @Override
    public void afterTestClass(TestContext testContext) throws Exception {
        System.out.println("MyListener.afterTestClass");
    }
}

Writing JUnit test

A TextExecutionListener can be registered via @TestExecutionListeners annotation:

package com.logicbig.example;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import static org.junit.jupiter.api.Assertions.assertTrue;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
@TestExecutionListeners(value = {MyListener.class,
        DependencyInjectionTestExecutionListener.class})
public class MyTests {

    @Autowired
    private MyBean myBean;

    @Test
    public void testDoSomething() {
        myBean.doSomething();
        assertTrue(true);
    }
}

Output

$ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] --------< com.logicbig.example:custom-test-execution-listener >---------
[INFO] Building custom-test-execution-listener 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ custom-test-execution-listener ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\spring-core-testing\custom-test-execution-listener\src\main\resources
[INFO]
[INFO] --- compiler:3.3:compile (default-compile) @ custom-test-execution-listener ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 2 source files to D:\example-projects\spring-core-testing\custom-test-execution-listener\target\classes
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ custom-test-execution-listener ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory D:\example-projects\spring-core-testing\custom-test-execution-listener\src\test\resources
[INFO]
[INFO] --- compiler:3.3:testCompile (default-testCompile) @ custom-test-execution-listener ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 2 source files to D:\example-projects\spring-core-testing\custom-test-execution-listener\target\test-classes
[INFO]
[INFO] --- surefire:3.2.5:test (default-test) @ custom-test-execution-listener ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[WARNING] file.encoding cannot be set as system property, use <argLine>-Dfile.encoding=...</argLine> instead
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.logicbig.example.MyTests
MyListener.beforeTestClass()
MyListener.prepareTestInstance()
MyListener.beforeTestMethod()
-- in MyBean.doSomething() method --
MyListener.afterTestMethod()
MyListener.afterTestClass
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.468 s -- in com.logicbig.example.MyTests
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.535 s
[INFO] Finished at: 2026-02-06T11:01:38+08:00
[INFO] ------------------------------------------------------------------------

Note that, we also used DependencyInjectionTestExecutionListener in above example, that's because when we specify our own listeners with @TestExecutionListeners, all default listeners are overridden. In above example, We needed DependencyInjectionTestExecutionListener as we used @Autowired.

Merging TestExecutionListeners

As mentioned above, when we specify our own listeners with @TestExecutionListeners, all default listeners are overridden. One way to utilize default listeners is to include them manually in the value attribute of this annotation (as we saw above). Another way is to use 'mergeMode' of this annotation, which will not replace the default listeners. The following snippet shows the usage:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
@TestExecutionListeners(value = MyListener.class,
      mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
public class MyTests {
    ....
}

The default value of 'mergeMode' is MergeMode.REPLACE_DEFAULTS.

Example Project

Dependencies and Technologies Used:

  • spring-context 7.0.4 (Spring Context)
     Version Compatibility: 5.0.0.RELEASE - 7.0.4Version List
    ×

    Version compatibilities of spring-context with this example:

    • 5.0.0.RELEASE
    • 5.0.1.RELEASE
    • 5.0.2.RELEASE
    • 5.0.3.RELEASE
    • 5.0.4.RELEASE
    • 5.0.5.RELEASE
    • 5.0.6.RELEASE
    • 5.0.7.RELEASE
    • 5.0.8.RELEASE
    • 5.0.9.RELEASE
    • 5.0.10.RELEASE
    • 5.0.11.RELEASE
    • 5.0.12.RELEASE
    • 5.0.13.RELEASE
    • 5.0.14.RELEASE
    • 5.0.15.RELEASE
    • 5.0.16.RELEASE
    • 5.0.17.RELEASE
    • 5.0.18.RELEASE
    • 5.0.19.RELEASE
    • 5.0.20.RELEASE
    • 5.1.0.RELEASE
    • 5.1.1.RELEASE
    • 5.1.2.RELEASE
    • 5.1.3.RELEASE
    • 5.1.4.RELEASE
    • 5.1.5.RELEASE
    • 5.1.6.RELEASE
    • 5.1.7.RELEASE
    • 5.1.8.RELEASE
    • 5.1.9.RELEASE
    • 5.1.10.RELEASE
    • 5.1.11.RELEASE
    • 5.1.12.RELEASE
    • 5.1.13.RELEASE
    • 5.1.14.RELEASE
    • 5.1.15.RELEASE
    • 5.1.16.RELEASE
    • 5.1.17.RELEASE
    • 5.1.18.RELEASE
    • 5.1.19.RELEASE
    • 5.1.20.RELEASE
    • 5.2.0.RELEASE
    • 5.2.1.RELEASE
    • 5.2.2.RELEASE
    • 5.2.3.RELEASE
    • 5.2.4.RELEASE
    • 5.2.5.RELEASE
    • 5.2.6.RELEASE
    • 5.2.7.RELEASE
    • 5.2.8.RELEASE
    • 5.2.9.RELEASE
    • 5.2.10.RELEASE
    • 5.2.11.RELEASE
    • 5.2.12.RELEASE
    • 5.2.13.RELEASE
    • 5.2.14.RELEASE
    • 5.2.15.RELEASE
    • 5.2.16.RELEASE
    • 5.2.17.RELEASE
    • 5.2.18.RELEASE
    • 5.2.19.RELEASE
    • 5.2.20.RELEASE
    • 5.2.21.RELEASE
    • 5.2.22.RELEASE
    • 5.2.23.RELEASE
    • 5.2.24.RELEASE
    • 5.2.25.RELEASE
    • 5.3.0
    • 5.3.1
    • 5.3.2
    • 5.3.3
    • 5.3.4
    • 5.3.5
    • 5.3.6
    • 5.3.7
    • 5.3.8
    • 5.3.9
    • 5.3.10
    • 5.3.11
    • 5.3.12
    • 5.3.13
    • 5.3.14
    • 5.3.15
    • 5.3.16
    • 5.3.17
    • 5.3.18
    • 5.3.19
    • 5.3.20
    • 5.3.21
    • 5.3.22
    • 5.3.23
    • 5.3.24
    • 5.3.25
    • 5.3.26
    • 5.3.27
    • 5.3.28
    • 5.3.29
    • 5.3.30
    • 5.3.31
    • 5.3.32
    • 5.3.33
    • 5.3.34
    • 5.3.35
    • 5.3.36
    • 5.3.37
    • 5.3.38
    • 5.3.39
    • Compatible Java Version: 17+
    • 6.0.0
    • 6.0.1
    • 6.0.2
    • 6.0.3
    • 6.0.4
    • 6.0.5
    • 6.0.6
    • 6.0.7
    • 6.0.8
    • 6.0.9
    • 6.0.10
    • 6.0.11
    • 6.0.12
    • 6.0.13
    • 6.0.14
    • 6.0.15
    • 6.0.16
    • 6.0.17
    • 6.0.18
    • 6.0.19
    • 6.0.20
    • 6.0.21
    • 6.0.22
    • 6.0.23
    • 6.1.0
    • 6.1.1
    • 6.1.2
    • 6.1.3
    • 6.1.4
    • 6.1.5
    • 6.1.6
    • 6.1.7
    • 6.1.8
    • 6.1.9
    • 6.1.10
    • 6.1.11
    • 6.1.12
    • 6.1.13
    • 6.1.14
    • 6.1.15
    • 6.1.16
    • 6.1.17
    • 6.1.18
    • 6.1.19
    • 6.1.20
    • 6.1.21
    • 6.2.0
    • 6.2.1
    • 6.2.2
    • 6.2.3
    • 6.2.4
    • 6.2.5
    • 6.2.6
    • 6.2.7
    • 6.2.8
    • 6.2.9
    • 6.2.10
    • 6.2.11
    • 6.2.12
    • 6.2.13
    • 6.2.14
    • 6.2.15
    • 6.2.16
    • 7.0.0
    • 7.0.1
    • 7.0.2
    • 7.0.3
    • 7.0.4

    Versions in green have been tested.

  • spring-test 7.0.4 (Spring TestContext Framework)
  • junit-jupiter-engine 6.0.2 (Module "junit-jupiter-engine" of JUnit)
  • JDK 25
  • Maven 3.9.11

Spring Core Testing - TestExecutionListener Example Select All Download
  • custom-test-execution-listener
    • src
      • main
        • java
          • com
            • logicbig
              • example
      • test
        • java
          • com
            • logicbig
              • example
                • MyListener.java

    See Also

    Join