Close

Spring Core Testing - Context Customizers

[Last Updated: Feb 6, 2026]

In Spring Core testing, the ContextCustomizer interface allows test infrastructure to modify the ApplicationContext after bean definitions are loaded but before the context is refreshed.

This mechanism is primarily used to support advanced testing features such as dynamic bean registration, environment mutation, or conditional infrastructure setup without polluting user configuration classes.

Definition of ContextCustomizer

Version: 7.0.3
 package org.springframework.test.context;
 @FunctionalInterface
 public interface ContextCustomizer {
     void customizeContext(ConfigurableApplicationContext context, 1
                           MergedContextConfiguration mergedConfig);
 }
1Customize the supplied ConfigurableApplicationContext after bean definitions have been loaded into the context but before the context has been refreshed.

Why ContextCustomizer is needed

Traditional configuration mechanisms like @Configuration and @TestPropertySource operate at a declarative level. ContextCustomizer exists to support programmatic, test-driven customization of the test context lifecycle.

ContextCustomizerFactory

ContextCustomizerFactory is the factory for creating ContextCustomizers. All we need to do is to implement this interface.

Definition of ContextCustomizerFactory

Version: 7.0.3
 package org.springframework.test.context;
 @FunctionalInterface
 public interface ContextCustomizerFactory {
     ContextCustomizer createContextCustomizer(Class<?> testClass, 1
                                         List<ContextConfigurationAttributes>
                                             configAttributes);
 }
1Create a ContextCustomizer that should be used to customize a org.springframework.context.ConfigurableApplicationContext before it is refreshed.

Registering the ContextCustomizerFactory implementation

The ContextCustomizerFactory can be registered with the Spring test framework in two primary ways:

  • Via the spring.factories file:
    Create or edit the file src/test/resources/META-INF/spring.factories and declare the factory class:
    org.springframework.test.context.ContextCustomizerFactory=com.example.MyCustomizerFactory

    Use this method if the factory to be registered globality for all tests.

  • Via the @ContextCustomizerFactories annotation:
    Annotate your test class with @ContextCustomizerFactories and specify the factory class:
    @ContextCustomizerFactories(MyCustomizerFactory.class)

Definition of ContextCustomizerFactories

Version: 7.0.3
 package org.springframework.test.context;
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Inherited
 public @interface ContextCustomizerFactories {
     @AliasFor("factories")
     Class<? extends ContextCustomizerFactory>[] value() default {}; 1
     @AliasFor("value")
     Class<? extends ContextCustomizerFactory>[] factories() default {}; 2
     boolean inheritFactories() default true; 3
     MergeMode mergeMode() default MergeMode.MERGE_WITH_DEFAULTS; 4
     enum MergeMode { 5
         MERGE_WITH_DEFAULTS, 6
         REPLACE_DEFAULTS 7
     }
 }
1Alias for #factories.
2The ContextCustomizerFactory implementations to register.
3Whether the configured set of #factories from superclasses and enclosing classes should be inherited.
4The merge mode to use when @ContextCustomizerFactories is declared on a class that does not inherit factories from a superclass or enclosing class.
5Enumeration of modes that dictate whether explicitly declared factories are merged with the default factories when @ContextCustomizerFactories is declared on a class that does not inherit factories from a superclass or enclosing class.
6Indicates that locally declared factories should be merged with the default factories.
7Indicates that locally declared factories should replace the default factories.

Example

package com.logicbig.example;

public class TestService {

    public String value() {
        return "customized";
    }
}

Implementing ContextCustomizer

package com.logicbig.example;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;

public class TestServiceContextCustomizer implements ContextCustomizer {

    @Override
    public void customizeContext(
            ConfigurableApplicationContext context,
            MergedContextConfiguration mergedConfig) {

        GenericApplicationContext gac =
                (GenericApplicationContext) context;
        gac.registerBean(TestService.class, TestService::new);
    }
}

Implementing ContextCustomizerFactory

package com.logicbig.example;

import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import java.util.List;

public class TestServiceContextCustomizerFactory
        implements ContextCustomizerFactory {

    @Override
    public ContextCustomizer createContextCustomizer(
            Class<?> testClass,
            List<ContextConfigurationAttributes> configAttributes) {
        return new TestServiceContextCustomizer();
    }
}

Test Class

package com.logicbig.example;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextCustomizerFactories;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringJUnitConfig
@ContextCustomizerFactories(
        TestServiceContextCustomizerFactory.class)
class ContextCustomizerFactoriesTest {

    @Autowired
    private TestService testService;

    @Test
    void verifyContextCustomizerInjection() {
        assertEquals("customized", testService.value());
    }

    @Configuration
    static class TestConfig{}
}

Output

$ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] --------------< com.logicbig.example:context-customizers >--------------
[INFO] Building context-customizers 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.1:resources (default-resources) @ context-customizers ---
[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\context-customizers\src\main\resources
[INFO]
[INFO] --- compiler:3.11.0:compile (default-compile) @ context-customizers ---
[INFO] No sources to compile
[INFO]
[INFO] --- resources:3.3.1:testResources (default-testResources) @ context-customizers ---
[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\context-customizers\src\test\resources
[INFO]
[INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ context-customizers ---
[INFO] Changes detected - recompiling the module! :source
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 4 source files with javac [debug target 25] to target\test-classes
[INFO]
[INFO] --- surefire:3.2.5:test (default-test) @ context-customizers ---
[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.ContextCustomizerFactoriesTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.257 s -- in com.logicbig.example.ContextCustomizerFactoriesTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.273 s
[INFO] Finished at: 2026-02-06T11:11:15+08:00
[INFO] ------------------------------------------------------------------------

Conclusion

In this example, the test passes without declaring the custom bean in any configuration class. The bean is registered programmatically by a ContextCustomizer, which is activated through a ContextCustomizerFactory registered using @ContextCustomizerFactories.

This confirms that the test context was customized before refresh, which is precisely the responsibility of the ContextCustomizer mechanism in Spring Test.

Example Project

Dependencies and Technologies Used:

  • spring-context 7.0.3 (Spring Context)
     Version Compatibility: 6.1.0 - 7.0.3Version List
    ×

    Version compatibilities of spring-context with this example:

    • 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
    • 7.0.0
    • 7.0.1
    • 7.0.2
    • 7.0.3

    Versions in green have been tested.

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

Spring Core Testing - Context Customizers Select All Download
  • context-customizers
    • src
      • test
        • java
          • com
            • logicbig
              • example
                • ContextCustomizerFactoriesTest.java

    See Also

    Join