Each TestContext provides context management and caching support for the test instance for which it is responsible. Test instances do not automatically access the configured ApplicationContext. However, if the test class implements the ApplicationContextAware interface, then a reference to the ApplicationContext is passed to the test instance. Note that AbstractJUnit4SpringContextTests and AbstractTestNGSpringContextTests implement ApplicationContextAware and therefore provide access to ApplicationContext automatically.

ApplicationContext marked with the @Autowired annotation

As an alternative to implementing the ApplicationContextAware interface you can inject an application context for your test class through the @Autowired annotation on a field or setter, as shown in the following example:

Java

@SpringJUnitConfig
class MyTest {
    @Autowired 
    ApplicationContext applicationContext;
    // class body...
}
  1. Inject ApplicationContext.
Kotlin

@SpringJUnitConfig
class MyTest {
    @Autowired 
    lateinit var applicationContext: ApplicationContext
    // class body...
}
  1. Inject ApplicationContext.

Similarly, if your test is configured to load WebApplicationContext, then you can inject the web application context into the test like this:

Java

@SpringJUnitWebConfig 
class MyWebAppTest {
    @Autowired 
    WebApplicationContext wac;
    // class body...
}
  1. Configure WebApplicationContext.
  2. Inject WebApplicationContext.
Kotlin

@SpringJUnitWebConfig 
class MyWebAppTest {
    @Autowired 
    lateinit var wac: WebApplicationContext
    // class body...
}
  1. Configure WebApplicationContext.
  2. Implement WebApplicationContext.

Dependency injection using the @Autowired annotation is provided by the DependencyInjectionTestExecutionListener listener, which is configured by default ( see "Dependency Injection for Test Benches").

Test classes using the TestContext framework do not need to extend any specific class or implement a specific interface to configure their application context. Instead, configuration is accomplished by declaring the @ContextConfiguration annotation at the class level. If the test class does not explicitly declare application context resource locations or component classes, the configured ContextLoader loader determines how to load the context from the default location or default configuration classes. In addition to context resource locations and component classes, application context can also be configured using application context initializers.

The following sections explain how to use the @ContextConfiguration annotation in Spring to configure test ApplicationContext using XML configuration files, Groovy scripts, component classes (usually classes with the @Configuration annotation) or context initializers. You can also implement and configure your own SmartContextLoader for advanced use cases.

Context configuration using XML resources

To load the ApplicationContext for your tests using XML configuration files, annotate your test class with @ContextConfiguration and configure the locations attribute with an array that contains configuration metadata resource location data XML. A regular or relative path (for example, context.xml) is treated as a classpath resource that refers to the package in which the test class is defined. A path starting with a slash is considered an absolute classpath location (for example, /org/example/config.xml). The path representing the URL of the resource (i.e. the path prefixed with classpath:, file:, http:, etc.) is used as is.

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "/app-config.xml" and
// "/test-config.xml" in the root of the classpath
@ContextConfiguration(locations={"/app-config.xml", "/test-config.xml"}) 
class MyTest {
    // class body...
}
  1. Set the location attribute in the list of XML files.
Kotlin

@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from " /app-config.xml" and
// "/test-config.xml" in the root of the classpath
@ContextConfiguration("/app-config.xml", "/test-config.xml") 
class MyTest {
    // class body...
}
  1. Set the locations attribute in a list of XML files.

The @ContextConfiguration annotation supports an alias for the locations attribute via the standard value attribute from Java. Thus, if you do not need to declare additional attributes in the @ContextConfiguration annotation, you can omit the locations attribute name declaration and declare resource locations using the shorthand format demonstrated in the following example:

Java

@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-config.xml"}) 
class MyTest {
    // class body...
}
  1. Specify XML files without using the location attribute.
Kotlin

@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-config.xml") 
class MyTest {
    // class body...
}
  1. Specify XML files without using the location attribute.

If you omit the location and value attributes in the @ContextConfiguration annotation, the TestContext framework will try to determine the default location of the XML resource. Specifically, GenericXmlContextLoader and GenericXmlWebContextLoader define a default location based on the name of the test class. If your class is called com.example.MyTest, GenericXmlContextLoader loads the application context from "classpath:com/example/MyTest-context.xml". The following example shows how to do this:

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/example/ MyTest-context.xml"
@ContextConfiguration 
class MyTest {
    // class body...
}
  1. Load the configuration from the default location.
Kotlin

@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration 
class MyTest {
    // class body...
}
  1. Load the configuration from the location by default.
Context configuration using Groovy scripts

To load ApplicationContext for your tests using Groovy scripts using Groovy Bean Definition DSL.
You can also annotate your test class with @ContextConfiguration and configure the locations or value attribute with an array containing the Groovy script resource locations. The semantics for finding resources for Groovy scripts are the same as for XML configuration files.

Activation support for Groovy scripts
Support for using Groovy scripts to load ApplicationContext into the Spring TestContext Framework is enabled automatically if Groovy is in the classpath.

The following example shows how to set the Groovy configuration files:

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from "/AppConfig.groovy" and
// "/TestConfig.groovy" in the root of the classpath
@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"}) 
class MyTest {
    // class body...
}
Kotlin

@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "/AppConfig.groovy" and
// "/TestConfig.groovy" in the root of the classpath
@ContextConfiguration("/AppConfig.groovy", "/TestConfig.Groovy") 
class MyTest {
    // class body...
}
  1. Specifying the location of Groovy configuration files.

If you omit the locations and value attributes from the @ContextConfiguration annotation, the TestContext framework will attempt to determine a default Groovy script. Specifically, GenericGroovyXmlContextLoader and GenericGroovyXmlWebContextLoader define a default location based on the name of the test class. If your class is named com.example.MyTest, the Groovy context loader will load your application context from "classpath:com/example/MyTestContext.groovy". The following example shows how to use the default:

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/ example/MyTestContext.groovy"
@ContextConfiguration 
class MyTest {
    // class body...
}
  1. Load the configuration from the default location.
Kotlin

@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTestContext.groovy"
@ContextConfiguration 
class MyTest {
    // class body...
}
  1. Load the configuration from the default location.
Declaring XML configuration and Groovy scripts at the same time

You can declare XML configuration files and Groovy scripts at the same time using the location or value attribute in the @ContextConfiguration annotation. If the configured resource's location path ends in .xml, it is loaded using the XmlBeanDefinitionReader. Otherwise, it is loaded using GroovyBeanDefinitionReader.

The following listing shows how to combine both options in an integration test:

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "/app-config.xml" and "/TestConfig.groovy"
@ContextConfiguration({ "/app-config.xml", "/ TestConfig.groovy" })
class MyTest {
    // class body...
}
Kotlin

@ExtendWith (SpringExtension::class)
// ApplicationContext will be loaded from
// "/app-config.xml" and "/TestConfig.groovy"
@ContextConfiguration("/app-config.xml", "/TestConfig.groovy")
class MyTest {
    // class body...
}
Context configuration using component classes

To load ApplicationContext for your tests using component classes (see: section "Java-based container configuration"), you can annotate your test class with @ContextConfiguration and configure the classes attribute with an array containing references to the component classes. The following example shows how to do this:

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from AppConfig and TestConfig
@ContextConfiguration(classes = { AppConfig.class, TestConfig.class}) 
class MyTest {
    // class body...
}
  1. Specifying component classes.
Kotlin

@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from AppConfig and TestConfig
@ContextConfiguration(classes = [AppConfig::class, TestConfig::class])  
class MyTest {
    // class body...
}
  1. Specifying component classes.
Component Classes

The term "component class" can refer to any of the following:

  • A class annotated with @Configuration.

  • A component (that is, a class marked with the annotations @Component, @Service, @Repository or other generic annotations).

  • A JSR-330 compliant class annotated with javax.inject annotations.

  • Any class that contains a method annotated with @Bean.

  • Any other class that is intended to be registered as a bean Spring (i.e. Spring bean in ApplicationContext), potentially taking advantage of automatic detection and binding of a single constructor without the use of annotations in Spring.

See javadoc at @Configuration and @Bean for more information on the configuration and semantics of bean classes, with particular attention to looking at annotation with @Bean in lightweight mode.

If you omit the classes attribute in the @ContextConfiguration annotation, the TestContext framework will try to determine the presence of default configuration classes. Specifically, the AnnotationConfigContextLoader and AnnotationConfigWebContextLoader loaders locate all static nested classes of a test class that meet the configuration class implementation requirements as specified in the javadoc at @Configuration. Please note that the configuration class name is arbitrary. Additionally, a test class can optionally contain more than one static nested configuration class. In the following example, the OrderServiceTest class declares a static nested configuration class named Config that is automatically used to load ApplicationContext for test class:

Java

@SpringJUnitConfig 
// ApplicationContext will be loaded from the
// static nested class Config
class OrderServiceTest {
    @Configuration
    static class Config {
        // this bean will be injected into the OrderServiceTest class
        @Bean OrderService orderService() {
            OrderService orderService = new OrderServiceImpl();
            // set properties, etc.
            return orderService;
        }
    }
    @Autowired
    OrderService orderService;
    @Test
    void testOrderService() {
        // testing orderService
    }
}
  1. Loading configuration information from a nested class Config.
Kotlin

@SpringJUnitConfig 
// ApplicationContext will be loaded from the nested Config class
class OrderServiceTest {
    @Autowired
    lateinit var orderService: OrderService
    @Configuration
    class Config {
        // this bean will be injected into class OrderServiceTest
        @Bean
        fun orderService(): OrderService {
            // set properties, etc.
            return OrderServiceImpl()
        }
    }
    @Test
    fun testOrderService() {
        // testing orderService
    }
}
  1. Loading configuration information from a nested Config class.
Combining XML, Groovy scripts, and component classes

Sometimes it is desirable to mix XML configuration files. Groovy scripts and component classes (usually classes marked with the @Configuration annotation) to configure the ApplicationContext for your tests. For example, if you are using XML configuration in a production environment, you may decide that it is worth resorting to classes marked with the @Configuration annotation to configure specific Spring-managed beans for your tests, or vice versa.

Furthermore, some third-party frameworks (such as Spring Boot) provide excellent support for loading ApplicationContext from different types of resources (such as XML configuration files, Groovy scripts, and annotated classes) at the same time @Configuration ). The Spring Framework has historically not supported this for standard deployments. Consequently, most SmartContextLoader implementations that the Spring Framework supplies in the spring-test module support only one resource type per test context. However, this does not mean that both options cannot be used. The exception to the general rule is that GenericGroovyXmlContextLoader and GenericGroovyXmlWebContextLoader support both XML configuration files and Groovy scripts. Moreover, third-party frameworks may support declaring both locations and classes via the @ContextConfiguration annotation, and with standard testing support in the TestContext framework, you have There are options described below.

If you need to use resource locations (such as XML or Groovy) and classes annotated with @Configuration to configure your tests, then you need to choose something - one of these as an entry point, and that selected class or location must include or import the others. For example, in XML or Groovy scripts, you can include classes annotated with @Configuration by using bean scanning or defining them as regular Spring beans, while in a class annotated with @Configuration, you can use the @ImportResource annotation to import XML configuration files or Groovy scripts. Note that this operating logic is semantically equivalent to how you configure your application in a production environment: In a production configuration, you define either a set of XML or Groovy resource locations, or a set of classes marked with the @Configuration annotation, from which your production ApplicationContext is loaded, however you still have the option to include or import another type of configuration.

Configuring contexts using context initializers

To configure ApplicationContext for your tests with context initializers, annotate your test class with @ContextConfiguration and configure the initializers attribute with an array containing class references, implementing ApplicationContextInitializer. The declared context initializers are then used to initialize the ConfigurableApplicationContext that is loaded for your tests. Note that the particular ConfigurableApplicationContext type supported by each declared initializer must be compatible with the ApplicationContext type that creates the SmartContextLoader used (usually GenericApplicationContext). Moreover, the order in which initializers are called depends on whether they implement the Ordered interface from Spring or are marked with the @Order annotation or the standard @Priority annotation . The following example shows how to use initializers:

Java

@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from TestConfig
// and initialized using TestAppCtxInitializer
@ContextConfiguration(
    classes = TestConfig.class,
    initializers = TestAppCtxInitializer.class) 
class MyTest {
    // class body...
} 
  1. Set configuration using a configuration class and initializer.
Kotlin

@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from TestConfig
// and initialized using TestAppCtxInitializer
@ContextConfiguration(
    classes = [TestConfig::class],
    initializers = [TestAppCtxInitializer::class ]) 
class MyTest {
    // class body...
}
  1. Specifying configuration using a configuration class and initializer.

It is also possible to completely omit the declaration of XML configuration files, Groovy scripts, or component classes in the @ContextConfiguration annotation and instead declare only the ApplicationContextInitializer classes, which will then be responsible for registering beans with the context - for example, by programmatically loading bean definitions from XML files or configuration classes . The following example shows how to do this:

Java

@ExtendWith(SpringExtension.class)
// The ApplicationContext will be initialized with the EntireAppInitializer
// which is assumed to be registers beans in the context
@ContextConfiguration(initializers = EntireAppInitializer.class) 
class MyTest {
    // class body...
}
  1. Set configuration using just one initializer.
Kotlin

@ExtendWith(SpringExtension::class)
// The ApplicationContext will be initialized using the EntireAppInitializer
// which is supposed to register beans in the context
@ContextConfiguration(initializers = [EntireAppInitializer::class]) 
class MyTest {
    // class body...
}
  1. Set configuration using just one initializer.
Inheriting context configuration

The @ContextConfiguration annotation supports booleans inheritLocations and inheritInitializers attributes that indicate whether resource locations or component classes and context initializers declared by superclasses should be inherited. The default value for both flags is true. This means that the test class inherits resource locations or component classes, as well as context initializers declared by any superclasses. Specifically, the resource locations or component classes for the test class are added to the list of resource locations or annotated classes declared as superclasses. Similarly, the initializers for a given test class are added to the set of initializers defined by the test superclasses. Thus, subclasses can extend resource locations, component classes, or context initializers.

If the inheritLocations or inheritInitializers attribute is in the @ContextConfiguratio.n annotation is set to false, then the resource locations or component classes and context initializers for the test class respectively "shadow" and effectively override the configuration defined by the superclasses.

Starting with Spring Framework 5.3, test configuration can also be inherited from enclosing classes. See "Test class configuration with @Nested annotation for details".

In the following example, which uses XML resource locations, ApplicationContext for ExtendedTest is loaded from base-config.xml and extended-config.xml in that order. Therefore, beans defined in extended-config.xml can override (that is, replace) beans defined in base-config.xml. The following example shows how one class can extend another and use both its own configuration file and the superclass's configuration file:

Java

@ExtendWith(SpringExtension.class )
// ApplicationContext will be loaded from "/base-config.xml"
// in the root classpath @ContextConfiguration("/base-config.xml") 
class BaseTest {
    // class body...
}
// ApplicationContext will be loaded from "/base-config.xml" and
// "/extended-config.xml" in the root classpath
@ContextConfiguration("/extended-config.xml") 
class ExtendedTest extends BaseTest {
    // class body...
}
  • Configuration file defined in the superclass.
  • Configuration file defined in the subclass.
Kotlin

@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from "/base-config.xml"
// in the root classpath
@ContextConfiguration("/base-config.xml") 
open class BaseTest {
    // class body...
}
// ApplicationContext will be loaded from "/base-config.xml" and
// "/extended-config.xml" in the root classpath
@ContextConfiguration("/extended-config.xml") 
class ExtendedTest : BaseTest () {
    // class body...
}
  1. Configuration file defined in the superclass.
  2. The configuration file defined in the subclass.

Similarly, in the following example, which uses component classes, ApplicationContext for ExtendedTest is loaded from the BaseConfig and ExtendedConfig classes in that order. Therefore, beans defined in ExtendedConfig can override (that is, replace) beans defined in BaseConfig. The following example shows how one class can extend another and use both its own configuration class and the superclass's configuration class:

Java

// ApplicationContext will be loaded from BaseConfig
@SpringJUnitConfig(BaseConfig.class) 
class BaseTest {
    // class body...
}
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig
@SpringJUnitConfig(ExtendedConfig.class) 
class ExtendedTest extends BaseTest {
    // class body...
}
  1. Configuration class defined in a superclass.
  2. Configuration class defined in a subclass.
Kotlin

// ApplicationContext will be loaded from BaseConfig
@SpringJUnitConfig(BaseConfig::class) 
open class BaseTest {
    // class body...
}
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig
@SpringJUnitConfig(ExtendedConfig::class) 
class ExtendedTest : BaseTest() {
    // class body...
}
  1. Configuration class, defined in the superclass.
  2. Configuration class defined in the subclass.

In the following example, which uses context initializers, ApplicationContext for ExtendedTest is initialized using BaseInitializer and ExtendedInitializer. Note, however, that the order in which initializers are called depends on whether they implement Spring's Ordered interface or whether they are marked with Spring's @Order annotation or the standard @Priority annotation. The following example shows how one class can extend another and use both its own initializer and the superclass's initializer:

Java

// ApplicationContext will be initialized by BaseInitializer
@SpringJUnitConfig (initializers = BaseInitializer.class) 
class BaseTest {
    // class body...
}
// ApplicationContext will be initialized by BaseInitializer
// and ExtendedInitializer
@SpringJUnitConfig(initializers = ExtendedInitializer.class) 
ExtendedTest extends BaseTest {
    // class body...
}
  1. Initializer defined in the superclass.
  2. Initializer defined in a subclass.
Kotlin

// ApplicationContext will be initialized BaseInitializer
@SpringJUnitConfig(initializers = [BaseInitializer::class]) 
open class BaseTest {
    // class body...
}
// ApplicationContext will be initialized by BaseInitializer
// and ExtendedInitializer
@SpringJUnitConfig(initializers = [ExtendedInitializer::class]) 
class ExtendedTest : BaseTest() {
    // class body...
}
  1. Initializer defined in the superclass.
  2. Initializer defined in the subclass.
Context configuration using environment profiles

Spring Framework offers first-class support for the concept of environments and profiles (aka "bean definition profiles"), and integration tests can be configured to activate specific profiles defining beans for various testing scenarios. This is achieved by annotating the test class with the @ActiveProfiles annotation and providing a list of profiles to be activated when the ApplicationContext is loaded for the test.

You can use the @ActiveProfiles annotation with any implementation of the SmartContextLoader SPI interface, but the @ActiveProfiles annotation is not supported with implementations greater than the old SPI interface ContextLoader.

Let's look at two examples with XML configuration and classes annotated with @Configuration:


<!-- app-config.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="...">
    <bean id="transferService"
          class="com.bank.service.internal.DefaultTransferService">
        <constructor-arg ref="accountRepository"/>
        <constructor-arg ref="feePolicy"/>
    </bean>
    <bean id="accountRepository"
          class="com.bank.repository.internal.JdbcAccountRepository">
        <constructor-arg ref="dataSource"/>
    </bean>
    <bean id="feePolicy"
          class="com.bank.service.internal.ZeroFeePolicy"/>
    <beans profile="dev">
        <jdbc:embedded-database id="dataSource">
            <jdbc:script
                    location="classpath:com/bank/config/sql/schema.sql"/>
            <jdbc:script
                    location="classpath:com/bank/config/sql/test-data.sql"/>
        </jdbc:embedded-database>
    </beans>
    <beans profile="production">
        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    </beans>
    <beans profile="default">
        <jdbc:embedded-database id="dataSource">
            <jdbc:script
                    location="classpath:com/bank/config/sql/schema.sql"/>
        </jdbc:embedded-database>
    </beans>
</beans>
Java

@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from " classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
    @Autowired
    TransferService transferService;
    @Test
    void testTransferService() {
        // testing transferService
    }
}
Kotlin

@ExtendWith(SpringExtension:: class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
class TransferServiceTest {
    @Autowired
    lateinit var transferService: TransferService
    @Test
    fun testTransferService() {
        // test transferService
    }
}

When TransferServiceTest is executed, its ApplicationContext is loaded from the configuration file app-config.xml in the root of the classpath. If you look at app-config.xml, you can see that the AccountRepository bean has a dependency on the DataSource bean. However, dataSource is not defined as a high-level bean. Instead, dataSource is defined three times: in the production profile, in the dev profile, and in the default profile.

By marking TransferServiceTest with the @ActiveProfiles("dev") annotation, we instruct the Spring TestContext Framework to load the ApplicationContext with active profiles set to {"dev"}. As a result, an embedded database is created, which is populated with test data, and the accountRepository bean is attached with a link to the development DataSource. This is probably what you want to achieve in an integration test.

Sometimes it makes sense to assign beans to the default profile. Beans in a profile are enabled by default only if no other profile has been specifically enabled. This can be used to define the "return" beans that will be used in the default application state. For example, you can explicitly specify the data source for the dev and production profiles, but define the data source in memory as the default source if neither is active.

The following code listings demonstrate how to implement the same configuration and integration test using classes with the @Configuration annotation instead of XML:

Java

@Configuration
@Profile("dev")
public class StandaloneDataConfig {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }
}
Kotlin

@Configuration
@Profile("dev")
class StandaloneDataConfig {
    @Bean
    fun dataSource(): DataSource {
        return EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("classpath:com/bank/config/sql/schema.sql")
                .addScript("classpath:com/bank/config/sql/test-data.sql")
                .build()
    }
}
Java

@Configuration
@Profile("production")
public class JndiDataConfig {
    @Bean(destroyMethod="")
    public DataSource dataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}
Kotlin

@Configuration
@Profile("production")
class JndiDataConfig {
    @Bean(destroyMethod = "")
    fun dataSource(): DataSource {
        val ctx = InitialContext()
        return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
    }
}
Java

@Configuration
@Profile("default")
public class DefaultDataConfig {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .build();
    }
}
Kotlin

@Configuration
@Profile("default")
class DefaultDataConfig {
    @Bean
    fun dataSource(): DataSource {
        return EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("classpath:com/bank/config/sql/schema.sql")
                .build()
    }
}
Java

@Configuration
public class TransferServiceConfig {
    @Autowired DataSource dataSource;
    @Bean
    public TransferService transferService() {
        return new DefaultTransferService(accountRepository(), feePolicy());
    }
    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }
    @Bean
    public FeePolicy feePolicy() {
        return new ZeroFeePolicy();
    }
}
Kotlin

@Configuration
class TransferServiceConfig {
    @Autowired
    lateinit var dataSource: DataSource
    @Bean
    fun transferService(): TransferService {
        return DefaultTransferService(accountRepository(), feePolicy())
    }
    @Bean
    fun accountRepository(): AccountRepository {
        return JdbcAccountRepository(dataSource)
    }
    @Bean
    fun feePolicy(): FeePolicy {
        return ZeroFeePolicy()
    }
}
Java

@SpringJUnitConfig({
        TransferServiceConfig.class,
        StandaloneDataConfig.class,
        JndiDataConfig.class,
        DefaultDataConfig.class})
@ActiveProfiles("dev")
class TransferServiceTest {
    @Autowired
    TransferService transferService;
    @Test
    void testTransferService() {
        // testing transferService
    }
}
Kotlin

@SpringJUnitConfig(
        TransferServiceConfig::class,
        StandaloneDataConfig::class,
        JndiDataConfig::class,
        DefaultDataConfig::class)
@ActiveProfiles("dev")
class TransferServiceTest {
    @Autowired
    lateinit var transferService: TransferService
    @Test
    fun testTransferService() {
        // testing transferService
    }
}

In this variant, we split the XML configuration into four independent classes, marked with the @Configuration annotation:

  • TransferServiceConfig: Gets dataSource via dependency injection using the @Autowired annotation.

  • StandaloneDataConfig: Defines dataSource for an embedded database, suitable for developer tests.

  • JndiDataConfig: Defines the dataSource that retrieved from JNDI in production.

  • DefaultDataConfig: Defines the dataSource for the default embedded database if the profile is not active .

As in the XML-based configuration example, we still annotate TransferServiceTest with the annotation @ActiveProfiles("dev" ), but this time we specify all four configuration classes using the @ContextConfiguration annotation. The body of the test class itself remains completely unchanged.

It often happens that one set of profiles is used in several test classes within the same project. Thus, to avoid duplicate declarations of the @ActiveProfiles annotation, you can declare the @ActiveProfiles annotation once for the main class, and subclasses will automatically inherit the configuration with the @ActiveProfiles annotation code> from the main class. In the following example, the declaration of the @ActiveProfiles annotation (as well as other annotations) has been moved to the abstract superclass AbstractIntegrationTest:

Starting with Spring Framework 5.3, test configuration can also inherit from enclosing classes. See "Test class configuration with @Nested annotation for details".
Java

@SpringJUnitConfig({
        TransferServiceConfig.class,
        StandaloneDataConfig.class,
        JndiDataConfig.class,
        DefaultDataConfig.class})
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
Kotlin

@SpringJUnitConfig(
        TransferServiceConfig::class,
        StandaloneDataConfig::class,
        JndiDataConfig::class,
        DefaultDataConfig::class)
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
Java

// profile "dev" inherits from superclass
class TransferServiceTest extends AbstractIntegrationTest {
    @Autowired
    TransferService transferService;
    @Test
    void testTransferService() {
        // testing transferService
    }
}
Kotlin

// profile "dev" inherits from the superclass
class TransferServiceTest : AbstractIntegrationTest() {
    @Autowired
    lateinit var transferService: TransferService
    @Test
    fun testTransferService() {
        // testing transferService
    }
}

The @ActiveProfiles annotation also supports the inheritProfiles attribute, which can be used to disable inheritance of active profiles, as shown in the following example:

Java

// profile "dev" is overridden to "production"
@ActiveProfiles(profiles = "production", inheritProfiles = false)
class ProductionTransferServiceTest extends AbstractIntegrationTest {
    // test body
}
Kotlin

// profile "dev" is overridden to "production"
@ActiveProfiles("production", inheritProfiles = false)
class ProductionTransferServiceTest : AbstractIntegrationTest() {
    // test body
}

Furthermore, sometimes it is necessary to resolve active profiles for tests programmatically, rather than declaratively - for example, based on:

  • The current operating system.

  • The fact that tests are running on the build server to ensure continuous integration

  • The presence of certain environment variables.

  • The presence of custom annotations at the class level.

  • Other cross-cutting functionality.

To programmatically resolve active bean definition profiles, you can implement a custom ActiveProfilesResolver and register it with the resolver attribute in the @ActiveProfiles annotation. For more information, see the corresponding javadoc. The following example demonstrates how to implement and register a custom OperatingSystemActiveProfilesResolver:

Java

// "dev" profile is overridden programmatically using a custom resolver
@ActiveProfiles(
       resolver = OperatingSystemActiveProfilesResolver.class,
       inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
    // test body
}
Kotlin

// profile "dev" is overridden programmatically using a custom resolver
@ActiveProfiles(
       resolver = OperatingSystemActiveProfilesResolver::class,
       inheritProfiles = false)
class TransferServiceTest : AbstractIntegrationTest() {
    // test body
}
Java

public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {
    @Override
    public String[] resolve(Class<?> testClass) {
        String profile = ...;
        // determine the profile value based on the operating system
        return new String[] {profile};
    }
}
Kotlin

class OperatingSystemActiveProfilesResolver : ActiveProfilesResolver {
    override fun resolve(testClass: Class<*>) : Array<String> {
        val profile: String = ...
        // determine the profile value based on the operating system
        return arrayOf(profile)
    }
}
Context configuration using test property sources

Spring Framework offers first-class support for the concept of environment with a hierarchy of property sources, so you can configure integration tests using test property sources. Unlike the @PropertySource annotation used in @Configuration classes, the @TestPropertySource annotation can be declared in a test class to declare resource locations for test files properties or built-in properties. These test property sources are added to the PropertySources set in the Environment for the ApplicationContext loaded for the annotated integration test.

You can use the @TestPropertySource annotation with any implementation of the SmartContextLoader SPI interface, but the @TestPropertySource annotation > is not supported when using the older SPI ContextLoader implementation.

SmartContextLoader implementations access pooled test property source values through the getPropertySourceLocations() methods and getPropertySourceProperties() in MergedContextConfiguration.

Declaring test property sources

You can configure test property files using the locations attribute or value in the @TestPropertySource annotation.

Both traditional and XML-based property file formats are supported - for example, "classpath:/com/example/test.properties" or "file:///path/to/file.xml".

Each path is interpreted as Resource from Spring. A simple path (for example, "test.properties") is considered a classpath resource that refers to the package in which the test class is defined. A path starting with a slash is treated as an absolute classpath resource (for example: "/org/example/test.xml"). The path that references the URL (for example, a path prefixed with classpath:, file:, or http:) is loaded using the specified resource protocol . Resource location wildcards (such as */.properties) are not allowed: Each location must represent exactly one .properties or .xml resource.

The following example uses a test properties file:

Java

@ContextConfiguration
@TestPropertySource("/test.properties") 
class MyIntegrationTests {
    // class body...
}
  1. Set the properties file with an absolute path.
Kotlin

@ContextConfiguration
@TestPropertySource("/test.properties" ) 
class MyIntegrationTests {
    // class body...
}
  1. Specify the properties file with an absolute path.

You can configure inline properties as key-value pairs using the properties attribute in the @TestPropertySource annotation, as shown in the following example. All key-value pairs are added to the enclosing Environment as one test PropertySource with the highest precedence level.

The supported syntax for key-value pairs is the same as and the syntax defined for entries in the Java properties file:

  • key=value

  • key:value

  • key value

In the following example, the two inline properties:

Java

@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"}) 
class MyIntegrationTests {
    // class body...
}
  1. Set two properties using two key-value syntaxes.
Kotlin

@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"]) 
class MyIntegrationTests {
    // class body...
}
  1. Set two properties using two key-value syntaxes.

Starting with Spring Framework 5.2, the @TestPropertySource annotation can be used as a repeating annotation. This means you can have multiple @TestPropertySource annotation declarations in one test class, with locations and properties from subsequent @TestPropertySource annotations will override properties from previous @TestPropertySource annotations.

It is also possible to declare multiple composite annotations for a test class, each meta-annotated with @ TestPropertySource, and all of these @TestPropertySource annotation declarations will contribute to your test property sources.

The @TestPropertySource annotations exposed directly are always take precedence over meta-presented @TestPropertySource annotations. In other words, locations and properties from a directly present @TestPropertySource annotation will override locations and properties from the @TestPropertySource annotation used as a meta annotation.

Default property file detection

If the @TestPropertySource annotation is declared as an empty annotation (that is, without explicit values for the locations or properties attributes), an attempt is made to discover the default properties file relative to the class that declared the annotation. For example, if the annotated test class is com.example.MyTest, then the corresponding default properties file is classpath:com/example/MyTest.properties. If the default value cannot be determined, an IllegalStateException exception will be thrown.

Precedence

Test properties have a higher level of precedence than properties defined in the operating system environment, Java system properties, or property sources added declaratively by the application using the @PropertySource annotation or programmatically. Thus, test properties can be used to selectively override properties loaded from system and application property sources. Additionally, inline properties have a higher level of precedence than properties loaded from resource locations. Note, however, that properties registered through the @DynamicPropertySource annotation have a higher precedence level than properties loaded through the @TestPropertySource annotation.

In the following example, the timezone and port properties, as well as any properties defined in the "/test.properties" file, override any properties of the same name defined in the system and application properties sources. Moreover, if the "/test.properties" file defines entries for the timezone and port properties, then they are overridden by the built-in properties declared with properties attribute. The following example shows how to set properties both in a file and inline:

Java

@ContextConfiguration
@TestPropertySource(
    locations = "/test.properties",
    properties = {"timezone = GMT", "port: 4242"}
)
class MyIntegrationTests {
    // class body...
}
Kotlin

@ContextConfiguration
@TestPropertySource("/test.properties",
        properties = ["timezone = GMT", "port: 4242"]
)
class MyIntegrationTests {
    // class body...
} 
Inheriting and overriding test property sources

The @TestPropertySource annotation supports the boolean attributes inheritLocations and inheritProperties that indicate whether resource locations should be inherited for property files and inline properties declared by superclasses. The default value for both flags is true. This means that the test class inherits locations and inline properties declared by any superclasses. Specifically, the locations and inline properties for the test class are added to the locations and inline properties declared by the superclasses. This way, subclasses can extend locations and inline properties. Note that properties that appear later "overshadow" (that is, override) properties of the same name that appear earlier. In addition, the above precedence rules also apply to inherited test property sources.

If the inheritLocations or inheritPropertie.s attribute is in the @TestPropertySource annotation is set to false, the locations or inline properties for the test class accordingly "shadow" and effectively override the configuration defined by the superclasses.

Starting with Spring Framework 5.3, test configuration can also inherit from enclosing classes. See "Test class configuration with @Nested annotation for details".

In the following example, the ApplicationContext for BaseTest is loaded only using the base.properties file as the source of the test properties. At the same time, the ApplicationContext for the ExtendedTest is loaded using the base.properties and extended.properties files as test sources properties. The following example shows how to define properties in both a subclass and its superclass using properties files:

Java

@TestPropertySource ("base.properties")
@ContextConfiguration
class BaseTest {
    // ...
}
@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest extends BaseTest {
    // ...
}
Kotlin

@TestPropertySource("base.properties")
@ContextConfiguration
open class BaseTest {
    // ...
}
@TestPropertySource("extended.properties" )
@ContextConfiguration
class ExtendedTest : BaseTest() {
    // ...
}

In the following example ApplicationContext for BaseTest is loaded using only the inline property key1. At the same time, the ApplicationContext for the ExtendedTest is loaded using the key1 and key2 inline properties. The following example shows how to define properties in both a subclass and its superclass using inline properties:

Java

@TestPropertySource(properties = "key1 = value1 ")
@ContextConfiguration
class BaseTest {
    // ...
}
@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
class ExtendedTest extends BaseTest {
    // ...
}
Kotlin

@TestPropertySource(properties = ["key1 = value1"])
@ContextConfiguration
open class BaseTest {
    // ...
}
@TestPropertySource(properties = [" key2 = value2"])
@ContextConfiguration
class ExtendedTest : BaseTest() {
    // ...
}
Context configuration using dynamic property sources

Starting with Spring Framework 5.2.5, the TestContext framework provides support for dynamic properties using the @DynamicPropertySource annotation. This annotation can be used in integration tests that need to add properties with dynamic values to the PropertySources set in the Environment for the ApplicationContext loaded for the integration test.

The @DynamicPropertySource annotation and its supporting infrastructure were originally designed to allow properties from tests based on Testcontainers could easily be exposed to Spring integration tests. However, this function can also be used with any form of external resource whose lifecycle is maintained outside the ApplicationContext test.

Unlike the @TestPropertySource annotation that is applied at the class level, @DynamicPropertySource must be applied to a static method that takes a single DynamicPropertyRegistry argument, which is used to add name-value pairs in Environment. The values are dynamic and are specified via Supplier, which is only called when the property is resolved. Typically, method references are used to specify values, as seen in the following example, which uses the Testcontainers project to manage a Redis container outside of ApplicationContext from Spring. The IP address and port of the managed Redis container are available to components in the ApplicationContext of the test through the redis.host and redis.port properties. These properties can be accessed through the Environment abstraction from Spring or implemented directly into Spring managed beans - for example, through @Value("${redis.host}".) annotations and @Value("${redis.port}"), respectively.

If the annotations @DynamicPropertySource is used in the main class and you find that tests in the subclasses have failed because dynamic properties change between subclasses, you may need to annotate the main class with @DirtiesContext so that each subclass is guaranteed to receive its own ApplicationContext with the correct dynamic properties.

Java

@SpringJUnitConfig(/* . .. */)
@Testcontainers
class ExampleIntegrationTests {
    @Container
    static RedisContainer redis = new RedisContainer();
    @DynamicPropertySource
    static void redisProperties(DynamicPropertyRegistry registry) {
        registry.add("redis.host", redis::getContainerIpAddress);
        registry.add("redis.port", redis::getMappedPort);
    }
    // tests...
}
Kotlin

@SpringJUnitConfig(/* ... */)
@Testcontainers
class ExampleIntegrationTests {
    companion object {
        @Container
        @JvmStatic
        val redis: RedisContainer = RedisContainer()
        @DynamicPropertySource
        @JvmStatic
        fun redisProperties(registry: DynamicPropertyRegistry) {
            registry.add("redis.host", redis::getContainerIpAddress)
            registry.add("redis.port", redis::getMappedPort)
        }
    }
    // tests...
}
Precedence

Dynamic properties have a higher precedence level than properties loaded from the @TestPropertySource annotation, the operating system environment, Java system properties, or property sources added declaratively by the application using the @PropertySource annotation or programmatically . Thus, dynamic properties can be used to selectively override properties loaded through the @TestPropertySource annotation, system property sources, and application property sources.

Loading WebApplicationContext

To instruct the TestContext framework to load WebApplicationContext instead of the standard ApplicationContext, you can annotate the corresponding test class with @WebAppConfiguration.

The presence of the @WebAppConfiguration annotation in your test class tells the TestContext Framework (TCF) that the WebApplicationContext (WAC) should be loaded for your integration tests. In the background, TCF ensures that a MockServletContext is created and passed to your test's WAC. By default, the base resource path for your MockServletContext is set to src/main/webapp. It is interpreted as a path relative to the root of your JVM (usually the path to your project). If you are familiar with the webapp directory structure in a Maven project, then you know that src/main/webapp is the default location for the root of your WAR. If you need to override this default value, you can specify an alternative path in the @WebAppConfiguration annotation (for example, @WebAppConfiguration("src/test/webapp")). If you need to reference the core resource path from the classpath rather than from the file system, you can use the classpath: prefix from Spring.

Note that testing support is for WebApplicationContext implementations in Spring is on par with support for standard ApplicationContext implementations. When testing with WebApplicationContext, you can freely declare XML configuration files, Groovy scripts, or classes marked with the @Configuration annotation using the @ContextConfiguration annotation. You can also freely use any other test annotations such as @ActiveProfiles, @TestExecutionListeners, @Sql, @Rollback and others.

The remaining examples in this section demonstrate some of the different configuration options for loading WebApplicationContext. The following example shows the TestContext framework's support for convention over configuration:

Java

@ExtendWith(SpringExtension.class)
// default "file:src/main/webapp" "
@WebAppConfiguration
// detects "WacTests-context.xml" in the same package
// or static nested classes with the @Configuration annotation
@ContextConfiguration
class WacTests {
    //...
}
Kotlin

@ExtendWith(SpringExtension::class)
// defaults to "file:src/main/webapp"
@WebAppConfiguration
// detects "WacTests-context. xml" in the same package
// or static nested classes with the @Configuration annotation
@ContextConfiguration
class WacTests {
    //...
}

If you mark test class using the @WebAppConfiguration annotation without specifying the path to the resource database, then the resource path actually takes on the default value file:src/main/webapp. Likewise, if you declare a @ContextConfiguration annotation without specifying the locations resources, component classes or initializers contexts, then Spring tries detect the presence of your configuration using conventions (i.e. WacTests-context.xml in the same package as the WacTests class or static nested classes annotated with @Configuration ).

The following example shows how to explicitly declare the path to a resource base using the @WebAppConfiguration annotation and the location of XML resources using the @ContextConfiguration annotation:

Java

@ExtendWith(SpringExtension.class)
// file system resource
@WebAppConfiguration("webapp")
// classpath resource
@ContextConfiguration(" /spring/test-servlet-config.xml")
class WacTests {
    //...
}
Kotlin

@ExtendWith(SpringExtension::class)
// file system resource
@WebAppConfiguration("webapp")
// classpath resource
@ContextConfiguration("/spring/test-servlet-config.xml")
class WacTests {
    //...
}

It is important to note here the different semantics for paths with these two annotations. By default, resource paths annotated with @WebAppConfiguration are filesystem-based, while resource locations annotated with @ContextConfiguration are based on classpath.

The following example shows that we can override the default resource semantics for both annotations by specifying a Spring resource prefix:

Java

@ExtendWith(SpringExtension.class)
// classpath resource
@WebAppConfiguration("classpath:test-web-resources")
// file system resource
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
class WacTests {
    //. ..
}
Kotlin

@ExtendWith(SpringExtension::class)
// classpath resource
@WebAppConfiguration(" classpath:test-web-resources")
// file system resource
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
class WacTests {
    //...
} 

Compare the comments in this example with the previous one.

Working with web mocks

To provide comprehensive support for web testing, the TestContext framework has a ServletTestExecutionListener that is enabled by default. When testing on WebApplicationContext this listener is TestExecutionListener sets the default thread-local state using RequestContextHolder from Spring Web before each test method and creates a MockHttpServletRequest, MockHttpServletResponse and ServletWebRequest based on the main resource path configured with @WebAppConfiguration. The ServletTestExecutionListener also ensures that MockHttpServletResponse and ServletWebRequest can be injected into the test instance, and when the test completes, it cleans up the thread's local state.

Once you've loaded the WebApplicationContext for your test, you may find that you need to interact with web mocks—for example, to set up a testbench or to run assertions after calling your web component. The following example shows which mock objects can be automatically discovered and associated with your test instance. Note that the WebApplicationContext and MockServletContext are cached for the entire test suite, while other mock objects are managed by each test method using the ServletTestExecutionListener.

Java

@SpringJUnitWebConfig
class WacTests {
    @Autowired
    WebApplicationContext wac; // cached
    @Autowired
    MockServletContext servletContext; // cached
    @Autowired
    MockHttpSession session;
    @Autowired
    MockHttpServletRequest request;
    @Autowired
    MockHttpServletResponse response;
    @Autowired
    ServletWebRequest webRequest;
    //...
}
Kotlin

@SpringJUnitWebConfig
class WacTests {
    @Autowired
    lateinit var wac: WebApplicationContext // cached
    @Autowired
    lateinit var servletContext: MockServletContext // cached
    @Autowired
    lateinit var session: MockHttpSession
    @Autowired
    lateinit var request: MockHttpServletRequest
    @Autowired
    lateinit var response: MockHttpServletResponse
    @Autowired
    lateinit var webRequest: ServletWebRequest
    //...
}
Caching context

Once the TestContext framework has loaded the ApplicationContext (or WebApplicationContext) for a test, this context is cached and reused for all subsequent tests that declare the same unique context configuration within the same test suite. To understand how caching works, it is important to understand what is meant by "unique" and "test suite".

ApplicationContext can be uniquely identified by the combination of configuration parameters that is used to load it . Therefore, a unique combination of configuration parameters is used to generate the key under which the context is cached. The TestContext framework uses the following configuration parameters to construct the context cache key:

  • locations (with @ContextConfiguration)

  • classes (with @ContextConfiguration)

  • contextInitializerClasses (from @ContextConfiguration)

  • contextCustomizers (from ContextCustomizerFactory) - this includes methods , marked with the @DynamicPropertySource annotation, as well as various functions from the Spring Boot testing support tools, such as the @MockBean and @SpyBean annotations.

  • contextLoader (with @ContextConfiguration)

  • parent (with @ContextHierarchy)

  • activeProfiles (with @ActiveProfiles)

  • propertySourceLocations (with @TestPropertySource)

  • propertySourceProperties (with @TestPropertySource)

  • resourceBasePath (with @WebAppConfiguration )

For example, if TestClassA specifies {"app-config.xml", "test-config.xml".} for a locations (or value) attribute annotated with @ContextConfiguration, the TestContext framework loads the corresponding ApplicationContext and saves it in a static key context cache that is based solely on these locations. So if TestClassB also defines {"app-config.xml", "test-config.xml"} for its locations (explicitly or implicitly via inheritance), but not defines a @WebAppConfiguration annotation, a different ContextLoader, different active profiles, different context initializers, different test property sources, or a different parent context, then the same ApplicationContext is shared between both test classes. This means that resources are spent loading the application context only once (for each test suite), and subsequent test executions are much faster.

Test Suites and Forked Processes

Spring's TestContext framework stores application contexts in a static cache. This means that the context is literally stored in a static variable. In other words, if tests are run in separate processes, the static cache is cleared between each test execution, effectively disabling the caching mechanism.

To take advantage of the caching mechanism, all tests must be run within the same process or test suite. This can be achieved by running all tests as a group in the IDE. Likewise, when running tests using a build framework such as Ant, Maven, or Gradle, it is important to ensure that the build framework does not fork between tests. For example, if forkMode for the Maven Surefire plugin is set to always or pertest, the TestContext framework will not be able to cache application contexts between test classes, resulting in a significantly slower build process.

The size of the context cache is limited, with a default maximum size of 32. When the maximum size is reached, a time-in-use (LRU) eviction policy is used to evict and close outdated contexts. You can configure the maximum size from the command line or build script by setting a JVM system property called spring.test.context.cache.maxSize. Alternatively, you can set the same property through the SpringProperties.

Because the large number of application contexts loaded in a competitive test suite can cause the suite to take too long to execute, it is often useful to know exactly how many contexts have been loaded and cached. To view statistics for the underlying context cache, you can set the logging level for the org.springframework.test.context.cache logging category to DEBUG.

In the unlikely event that a test corrupts the application context and requires a reload (for example, by changing a bean definition or the state of an application object), you can annotate your test class or test method with @DirtiesContext (see the @DirtiesContext annotation description) in the section "Annotations for testing in Spring"). This tells Spring to remove the context from the cache and rebuild the application context before running the next test that requires the same application context. Note that support for the @DirtiesContext annotation is provided by the DirtiesContextBeforeModesTestExecutionListener and DirtiesContextTestExecutionListener listeners, which are enabled by default.

ApplicationContext lifecycle and console output

If you need to debug a test run using the Spring TestContext Framework, you can It may be useful to analyze the console output (that is, the output to the SYSOUT and SYSERR streams). Some built-in build tools and IDEs can associate console output with a specific test; however, you won't be able to easily associate some console output with a specific test.

With regard to console output triggered by the Spring Framework itself or by components registered in the ApplicationContext, it is important to understand the ApplicationContext that was loaded by the Spring TestContext Framework in the test suite.

The ApplicationContext for a test is typically loaded when preparing an instance of the test class - for example, to perform dependency injection on fields test instance, marked with the @Autowired annotation. This means that any console output fired during ApplicationContext initialization typically cannot be associated with a separate test method. However, if the context is closed immediately before the test method is executed according to the semantics of the annotation @DirtiesContext, a new context instance will be loaded immediately before the test method is executed. In the latter scenario, the IDE or built-in build tool could potentially associate console output with a separate test method.

ApplicationContext for a test could be closed in one of the following scenarios.

ApplicationContext for a test could be closed in one of the following scenarios.

  • The context is closed according to the semantics of the @DirtiesContext annotation.

  • The context is closed because it was automatically deleted from the cache according to the LRU eviction policy.

  • The context is closed via the JVM termination hook when the JVM for the test suite terminates.

If the context is closed according to the semantics of the @DirtiesContext annotation after a particular test method, the IDE or built-in build tool could potentially associate console output with the individual test method. If the context is closed according to @DirtiesContext semantics after the test class is executed, any console output that fires while the ApplicationContext is closed cannot be associated with a separate test method. Additionally, any console output triggered during the shutdown phase via a shutdown hook from the JVM cannot be associated with a separate test method.

If the ApplicationContext from Spring is closed via a shutdown hook from JVM callbacks executed during the shutdown phase are executed on a thread named SpringContextShutdownHook. Thus, if you want to disable the console output triggered by closing the ApplicationContext through the shutdown hook from the JVM, you can register a custom filter with the logging framework to ignore any logging initiated by that thread.

Context hierarchies

When writing integration tests that use a loaded ApplicationContext from Spring, it is often It may be sufficient to test against one context. However, there are times when it is useful or even necessary to test against a hierarchy of ApplicationContext instances. For example, if you are developing a web application in Spring MVC, then you typically have a root WebApplicationContext loaded by a ContextLoaderListener from Spring, and a child WebApplicationContext , loaded by DispatcherServlet from Spring. This results in a parent-child context hierarchy where common infrastructure components and configuration are declared in the root context and consumed in the child context by web-specific components. Another use case occurs in Spring Batch applications, where there is often a parent context that provides configuration for the general batch infrastructure, and a child context for configuration of a specific batch job.

You can write integration tests that use hierarchies of contexts by declaring a context configuration using the @ContextHierarchy annotation, either for a separate test class or within a hierarchy of test classes. If a context hierarchy is declared for multiple classes in the test class hierarchy, it is also possible to combine or override the context configuration for a specific named level in the context hierarchy. When merging configuration for a given level of the hierarchy, the type of configuration resources (that is, XML configuration files or component classes) must be consistent. Otherwise, it is perfectly acceptable to have different levels in the context hierarchy configured using different types of resources.

The remaining JUnit Jupiter-based examples in this section illustrate common configuration scenarios for integration tests that require the use of context hierarchies.

The only test class with a context hierarchy

ControllerIntegrationTests represents a typical integration testing scenario for a web application in Spring MVC, declaring a hierarchy of contexts consisting of two levels; one for the root WebApplicationContext (loaded using the TestAppConfig @Configuration class) and the other for the WebApplicationContext dispatcher servlet (loaded using the WebConfig @Configuration class). The WebApplicationContext that is automatically discovered and associated with the test instance is the context for the child context (that is, the lowest-level context in the hierarchy). The following listing shows this configuration scenario:

Java
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
    @ContextConfiguration(classes = TestAppConfig.class),
    @ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {
    @Autowired
    WebApplicationContext wac;
    // ...
}
Kotlin
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextHierarchy(
    ContextConfiguration(classes = [TestAppConfig::class]),
    ContextConfiguration(classes = [WebConfig::class]))
class ControllerIntegrationTests {
    @Autowired
    lateinit var wac: WebApplicationContext
    // ...
}
Class hierarchy with implicit parent context

The test classes in this example define a hierarchy of contexts within a hierarchy of test classes. AbstractWebTests declares the configuration for the root WebApplicationContext in a Spring-based web application. Note, however, that AbstractWebTests does not declare the @ContextHierarchy annotation. Therefore, AbstractWebTests subclasses can optionally participate in the context hierarchy or follow the standard @ContextConfiguration annotation semantics. SoapWebServiceTests and RestWebServiceTests extend AbstractWebTests and define a hierarchy of contexts using the @ContextHierarchy annotation. As a result, three application contexts are loaded (one for each @ContextConfiguration annotation declaration), and the application context loaded based on the configuration in AbstractWebTests is set as the parent context for each contexts loaded for specific subclasses. The following listing shows this configuration scenario:

Java
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
public abstract class AbstractWebTests {}
@ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml"))
public class SoapWebServiceTests extends AbstractWebTests {}
@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml"))
public class RestWebServiceTests extends AbstractWebTests {}
Kotlin
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
abstract class AbstractWebTests
@ContextHierarchy(ContextConfiguration("/spring/soap-ws-config.xml"))
class SoapWebServiceTests : AbstractWebTests()
@ContextHierarchy(ContextConfiguration("/spring/rest-ws-config.xml"))
class RestWebServiceTests : AbstractWebTests()
Class hierarchy with combined context hierarchy configuration

The classes in this example demonstrate the use of named hierarchy levels to aggregate configuration for specific levels in a context hierarchy. BaseTests defines two levels in the hierarchy, namely parent and child. ExtendedTests extends BaseTests and instructs the Spring TestContext Framework to bundle the context configuration for the child level of the hierarchy, ensuring that the names declared in the name attribute in the @ContextConfiguration annotation were child. This loads three application contexts: one for /app-config.xml, one for /user-config.xml, and one for {"/user- config.xml", "/order-config.xml"}. As in the previous example, the application context loaded from /app-config.xml is set as the parent context of the contexts loaded from /user-config.xml and {"/user-config.xml", "/order-config.xml"}. The following listing shows this configuration scenario:

Java
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
    @ContextConfiguration(name = "parent", locations = "/app-config.xml"),
    @ContextConfiguration(name = "child", locations = "/user-config.xml")
})
class BaseTests {}
@ContextHierarchy(
    @ContextConfiguration(name = "child", locations = "/order-config.xml")
)
class ExtendedTests extends BaseTests {}
Kotlin
@ExtendWith(SpringExtension::class)
@ContextHierarchy(
    ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
    ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
open class BaseTests {}
@ContextHierarchy(
    ContextConfiguration(name = "child", locations = ["/order-config.xml"])
)
class ExtendedTests : BaseTests() {}
Class hierarchy with overridden context hierarchy configuration

Unlike the previous example, this example demonstrates how to override the configuration for a given named level in the context hierarchy by setting the inheritLocations flag in the @ContextConfiguration annotation to false. Therefore, the application context for ExtendedTests is loaded only from /test-user-config.xml, and its parent is the context loaded from /app-config.xml. The following listing shows this configuration scenario:

Java
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
    @ContextConfiguration(name = "parent", locations = "/app-config.xml"),
    @ContextConfiguration(name = "child", locations = "/user-config.xml")
})
class BaseTests {}
@ContextHierarchy(
    @ContextConfiguration(
        name = "child",
        locations = "/test-user-config.xml",
        inheritLocations = false
))
class ExtendedTests extends BaseTests {}
Kotlin
@ExtendWith(SpringExtension::class)
@ContextHierarchy(
    ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
    ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
open class BaseTests {}
@ContextHierarchy(
        ContextConfiguration(
                name = "child",
                locations = ["/test-user-config.xml"],
                inheritLocations = false
        ))
class ExtendedTests : BaseTests() {}
If you use the @DirtiesContext annotation in a test whose context is configured as part of a context hierarchy, you can use the hierarchyMode flag to control the clearing of the context cache. For more information, see the description of the @DirtiesContext annotation in the section "Annotations for testing in Spring" and javadoc by annotation @DirtiesContext.