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.
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:
@SpringJUnitConfig
class MyTest {
@Autowired
ApplicationContext applicationContext;
// class body...
}
- Inject
ApplicationContext
.
@SpringJUnitConfig
class MyTest {
@Autowired
lateinit var applicationContext: ApplicationContext
// class body...
}
- Inject
ApplicationContext
.
Similarly, if your test is configured to load WebApplicationContext
, then you can inject the web
application context into the test like this:
@SpringJUnitWebConfig
class MyWebAppTest {
@Autowired
WebApplicationContext wac;
// class body...
}
- Configure
WebApplicationContext
. - Inject
WebApplicationContext
.
@SpringJUnitWebConfig
class MyWebAppTest {
@Autowired
lateinit var wac: WebApplicationContext
// class body...
}
- Configure
WebApplicationContext
. - 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.
@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...
}
- Set the location attribute in the list of XML files.
@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...
}
- 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:
@ExtendWith(SpringExtension.class)
@ContextConfiguration({"/app-config.xml", "/test-config.xml"})
class MyTest {
// class body...
}
- Specify XML files without using the
location
attribute.
@ExtendWith(SpringExtension::class)
@ContextConfiguration("/app-config.xml", "/test-config.xml")
class MyTest {
// class body...
}
- 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:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/example/ MyTest-context.xml"
@ContextConfiguration
class MyTest {
// class body...
}
- Load the configuration from the default location.
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration
class MyTest {
// class body...
}
- 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.
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:
@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...
}
@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...
}
- 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:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "classpath:com/ example/MyTestContext.groovy"
@ContextConfiguration
class MyTest {
// class body...
}
- Load the configuration from the default location.
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTestContext.groovy"
@ContextConfiguration
class MyTest {
// class body...
}
- Load the configuration from the default location.
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:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from
// "/app-config.xml" and "/TestConfig.groovy"
@ContextConfiguration({ "/app-config.xml", "/ TestConfig.groovy" })
class MyTest {
// class body...
}
@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:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from AppConfig and TestConfig
@ContextConfiguration(classes = { AppConfig.class, TestConfig.class})
class MyTest {
// class body...
}
- Specifying component classes.
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from AppConfig and TestConfig
@ContextConfiguration(classes = [AppConfig::class, TestConfig::class])
class MyTest {
// class body...
}
- Specifying 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:
@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
}
}
- Loading configuration information from a nested class
Config
.
@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
}
}
- 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:
@ExtendWith(SpringExtension.class)
// ApplicationContext will be loaded from TestConfig
// and initialized using TestAppCtxInitializer
@ContextConfiguration(
classes = TestConfig.class,
initializers = TestAppCtxInitializer.class)
class MyTest {
// class body...
}
- Set configuration using a configuration class and initializer.
@ExtendWith(SpringExtension::class)
// ApplicationContext will be loaded from TestConfig
// and initialized using TestAppCtxInitializer
@ContextConfiguration(
classes = [TestConfig::class],
initializers = [TestAppCtxInitializer::class ])
class MyTest {
// class body...
}
- 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:
@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...
}
- Set configuration using just one initializer.
@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...
}
- 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.
@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:
@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.
@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...
}
- Configuration file defined in the superclass.
- 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:
// 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...
}
- Configuration class defined in a superclass.
- Configuration class defined in a subclass.
// 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...
}
- Configuration class, defined in the superclass.
- 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:
// 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...
}
- Initializer defined in the superclass.
- Initializer defined in a subclass.
// 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...
}
- Initializer defined in the superclass.
- 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.
@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>
@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
}
}
@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:
@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();
}
}
@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()
}
}
@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");
}
}
@Configuration
@Profile("production")
class JndiDataConfig {
@Bean(destroyMethod = "")
fun dataSource(): DataSource {
val ctx = InitialContext()
return ctx.lookup("java:comp/env/jdbc/datasource") as DataSource
}
}
@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();
}
}
@Configuration
@Profile("default")
class DefaultDataConfig {
@Bean
fun dataSource(): DataSource {
return EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build()
}
}
@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();
}
}
@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()
}
}
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
class TransferServiceTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// testing transferService
}
}
@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
: GetsdataSource
via dependency injection using the@Autowired
annotation.StandaloneDataConfig
: DefinesdataSource
for an embedded database, suitable for developer tests.JndiDataConfig
: Defines thedataSource
that retrieved from JNDI in production.DefaultDataConfig
: Defines thedataSource
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
:
@Nested
annotation for details".
@SpringJUnitConfig({
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
@SpringJUnitConfig(
TransferServiceConfig::class,
StandaloneDataConfig::class,
JndiDataConfig::class,
DefaultDataConfig::class)
@ActiveProfiles("dev")
abstract class AbstractIntegrationTest {
}
// profile "dev" inherits from superclass
class TransferServiceTest extends AbstractIntegrationTest {
@Autowired
TransferService transferService;
@Test
void testTransferService() {
// testing transferService
}
}
// 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:
// profile "dev" is overridden to "production"
@ActiveProfiles(profiles = "production", inheritProfiles = false)
class ProductionTransferServiceTest extends AbstractIntegrationTest {
// test body
}
// 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
:
// "dev" profile is overridden programmatically using a custom resolver
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver.class,
inheritProfiles = false)
class TransferServiceTest extends AbstractIntegrationTest {
// test body
}
// profile "dev" is overridden programmatically using a custom resolver
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver::class,
inheritProfiles = false)
class TransferServiceTest : AbstractIntegrationTest() {
// test body
}
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};
}
}
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:
@ContextConfiguration
@TestPropertySource("/test.properties")
class MyIntegrationTests {
// class body...
}
- Set the properties file with an absolute path.
@ContextConfiguration
@TestPropertySource("/test.properties" )
class MyIntegrationTests {
// class body...
}
- 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:
@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"})
class MyIntegrationTests {
// class body...
}
- Set two properties using two key-value syntaxes.
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port: 4242"])
class MyIntegrationTests {
// class body...
}
- 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:
@ContextConfiguration
@TestPropertySource(
locations = "/test.properties",
properties = {"timezone = GMT", "port: 4242"}
)
class MyIntegrationTests {
// class body...
}
@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.
@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:
@TestPropertySource ("base.properties")
@ContextConfiguration
class BaseTest {
// ...
}
@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest extends BaseTest {
// ...
}
@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:
@TestPropertySource(properties = "key1 = value1 ")
@ContextConfiguration
class BaseTest {
// ...
}
@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
class ExtendedTest extends BaseTest {
// ...
}
@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.
@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...
}
@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:
@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 {
//...
}
@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:
@ExtendWith(SpringExtension.class)
// file system resource
@WebAppConfiguration("webapp")
// classpath resource
@ContextConfiguration(" /spring/test-servlet-config.xml")
class WacTests {
//...
}
@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:
@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 {
//. ..
}
@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.
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
.
@SpringJUnitWebConfig
class WacTests {
@Autowired
WebApplicationContext wac; // cached
@Autowired
MockServletContext servletContext; // cached
@Autowired
MockHttpSession session;
@Autowired
MockHttpServletRequest request;
@Autowired
MockHttpServletResponse response;
@Autowired
ServletWebRequest webRequest;
//...
}
@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
(fromContextCustomizerFactory
) - 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.
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.
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.
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:
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = TestAppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {
@Autowired
WebApplicationContext wac;
// ...
}
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextHierarchy(
ContextConfiguration(classes = [TestAppConfig::class]),
ContextConfiguration(classes = [WebConfig::class]))
class ControllerIntegrationTests {
@Autowired
lateinit var wac: WebApplicationContext
// ...
}
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:
@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 {}
@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()
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:
@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 {}
@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() {}
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:
@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 {}
@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() {}
@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
.