On this page
15. Integration Testing
It is important to be able to perform some integration testing without requiring deployment to your application server or connecting to other enterprise infrastructure. This will enable you to test things such as:
- The correct wiring of your Spring IoC container contexts.
- Data access using JDBC or an ORM tool. This would include such things as the correctness of SQL statements, Hibernate queries, JPA entity mappings, etc.
The Spring Framework provides first-class support for integration testing in the spring-test
module. The name of the actual JAR file might include the release version and might also be in the long org.springframework.test
form, depending on where you get it from (see the section on Dependency Management for an explanation). This library includes the org.springframework.test
package, which contains valuable classes for integration testing with a Spring container. This testing does not rely on an application server or other deployment environment. Such tests are slower to run than unit tests but much faster than the equivalent Selenium tests or remote tests that rely on deployment to an application server.
In Spring 2.5 and later, unit and integration testing support is provided in the form of the annotation-driven Spring TestContext Framework. The TestContext framework is agnostic of the actual testing framework in use, thus allowing instrumentation of tests in various environments including JUnit, TestNG, and so on.
Spring’s integration testing support has the following primary goals:
- To manage Spring IoC container caching between test execution.
- To provide Dependency Injection of test fixture instances.
- To provide transaction management appropriate to integration testing.
- To supply Spring-specific base classes that assist developers in writing integration tests.
The next few sections describe each goal and provide links to implementation and configuration details.
The Spring TestContext Framework provides consistent loading of Spring ApplicationContext
s and WebApplicationContext
s as well as caching of those contexts. Support for the caching of loaded contexts is important, because startup time can become an issue — not because of the overhead of Spring itself, but because the objects instantiated by the Spring container take time to instantiate. For example, a project with 50 to 100 Hibernate mapping files might take 10 to 20 seconds to load the mapping files, and incurring that cost before running every test in every test fixture leads to slower overall test runs that reduce developer productivity.
Test classes typically declare either an array of resource locations for XML or Groovy configuration metadata — often in the classpath — or an array of annotated classes that is used to configure the application. These locations or classes are the same as or similar to those specified in web.xml
or other configuration files for production deployments.
By default, once loaded, the configured ApplicationContext
is reused for each test. Thus the setup cost is incurred only once per test suite, and subsequent test execution is much faster. In this context, the term test suite means all tests run in the same JVM — for example, all tests run from an Ant, Maven, or Gradle build for a given project or module. In the unlikely case that a test corrupts the application context and requires reloading — for example, by modifying a bean definition or the state of an application object — the TestContext framework can be configured to reload the configuration and rebuild the application context before executing the next test.
See Section 15.5.4, “Context management” and the section called “Context caching” with the TestContext framework.
When the TestContext framework loads your application context, it can optionally configure instances of your test classes via Dependency Injection. This provides a convenient mechanism for setting up test fixtures using preconfigured beans from your application context. A strong benefit here is that you can reuse application contexts across various testing scenarios (e.g., for configuring Spring-managed object graphs, transactional proxies, DataSource
s, etc.), thus avoiding the need to duplicate complex test fixture setup for individual test cases.
As an example, consider the scenario where we have a class, HibernateTitleRepository
, that implements data access logic for a Title
domain entity. We want to write integration tests that test the following areas:
- The Spring configuration: basically, is everything related to the configuration of the
HibernateTitleRepository
bean correct and present? - The Hibernate mapping file configuration: is everything mapped correctly, and are the correct lazy-loading settings in place?
- The logic of the
HibernateTitleRepository
: does the configured instance of this class perform as anticipated?
See dependency injection of test fixtures with the TestContext framework.
One common issue in tests that access a real database is their effect on the state of the persistence store. Even when you’re using a development database, changes to the state may affect future tests. Also, many operations — such as inserting or modifying persistent data — cannot be performed (or verified) outside a transaction.
The TestContext framework addresses this issue. By default, the framework will create and roll back a transaction for each test. You simply write code that can assume the existence of a transaction. If you call transactionally proxied objects in your tests, they will behave correctly, according to their configured transactional semantics. In addition, if a test method deletes the contents of selected tables while running within the transaction managed for the test, the transaction will roll back by default, and the database will return to its state prior to execution of the test. Transactional support is provided to a test via a PlatformTransactionManager
bean defined in the test’s application context.
If you want a transaction to commit — unusual, but occasionally useful when you want a particular test to populate or modify the database — the TestContext framework can be instructed to cause the transaction to commit instead of roll back via the @Commit
annotation.
See transaction management with the TestContext framework.
The Spring TestContext Framework provides several abstract
support classes that simplify the writing of integration tests. These base test classes provide well-defined hooks into the testing framework as well as convenient instance variables and methods, which enable you to access:
- The
ApplicationContext
, for performing explicit bean lookups or testing the state of the context as a whole. - A
JdbcTemplate
, for executing SQL statements to query the database. Such queries can be used to confirm database state both prior to and after execution of database-related application code, and Spring ensures that such queries run in the scope of the same transaction as the application code. When used in conjunction with an ORM tool, be sure to avoid false positives.
In addition, you may want to create your own custom, application-wide superclass with instance variables and methods specific to your project.
See support classes for the TestContext framework.
The org.springframework.test.jdbc
package contains JdbcTestUtils
, which is a collection of JDBC related utility functions intended to simplify standard database testing scenarios. Specifically, JdbcTestUtils
provides the following static utility methods.
countRowsInTable(..)
: counts the number of rows in the given tablecountRowsInTableWhere(..)
: counts the number of rows in the given table, using the providedWHERE
clausedeleteFromTables(..)
: deletes all rows from the specified tablesdeleteFromTableWhere(..)
: deletes rows from the given table, using the providedWHERE
clausedropTables(..)
: drops the specified tables
Note that AbstractTransactionalJUnit4SpringContextTests
and AbstractTransactionalTestNGSpringContextTests
provide convenience methods which delegate to the aforementioned methods in JdbcTestUtils
.
The spring-jdbc
module provides support for configuring and launching an embedded database which can be used in integration tests that interact with a database. For details, see Section 19.8, “Embedded database support” and Section 19.8.5, “Testing data access logic with an embedded database”.
The Spring Framework provides the following set of Spring-specific annotations that you can use in your unit and integration tests in conjunction with the TestContext framework. Refer to the corresponding javadocs for further information, including default attribute values, attribute aliases, and so on.
@BootstrapWith
is a class-level annotation that is used to configure how the Spring TestContext Framework is bootstrapped. Specifically, @BootstrapWith
is used to specify a custom TestContextBootstrapper
. Consult the Bootstrapping the TestContext framework section for further details.
@ContextConfiguration
defines class-level metadata that is used to determine how to load and configure an ApplicationContext
for integration tests. Specifically, @ContextConfiguration
declares the application context resource locations
or the annotated classes
that will be used to load the context.
Resource locations are typically XML configuration files or Groovy scripts located in the classpath; whereas, annotated classes are typically @Configuration
classes. However, resource locations can also refer to files and scripts in the file system, and annotated classes can be component classes, etc.
@ContextConfiguration("/test-config.xml")
public class XmlApplicationContextTests {
// class body...
}
@ContextConfiguration(classes = TestConfig.class)
public class ConfigClassApplicationContextTests {
// class body...
}
As an alternative or in addition to declaring resource locations or annotated classes, @ContextConfiguration
may be used to declare ApplicationContextInitializer
classes.
@ContextConfiguration(initializers = CustomContextIntializer.class)
public class ContextInitializerTests {
// class body...
}
@ContextConfiguration
may optionally be used to declare the ContextLoader
strategy as well. Note, however, that you typically do not need to explicitly configure the loader since the default loader supports either resource locations
or annotated classes
as well as initializers
.
@ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class)
public class CustomLoaderXmlApplicationContextTests {
// class body...
}
Note | |
---|---|
|
See Section 15.5.4, “Context management” and the @ContextConfiguration
javadocs for further details.
@WebAppConfiguration
is a class-level annotation that is used to declare that the ApplicationContext
loaded for an integration test should be a WebApplicationContext
. The mere presence of @WebAppConfiguration
on a test class ensures that a WebApplicationContext
will be loaded for the test, using the default value of "file:src/main/webapp"
for the path to the root of the web application (i.e., the resource base path). The resource base path is used behind the scenes to create a MockServletContext
which serves as the ServletContext
for the test’s WebApplicationContext
.
@ContextConfiguration
@WebAppConfiguration
public class WebAppTests {
// class body...
}
To override the default, specify a different base resource path via the implicit value
attribute. Both classpath:
and file:
resource prefixes are supported. If no resource prefix is supplied the path is assumed to be a file system resource.
@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources")
public class WebAppTests {
// class body...
}
Note that @WebAppConfiguration
must be used in conjunction with @ContextConfiguration
, either within a single test class or within a test class hierarchy. See the @WebAppConfiguration
javadocs for further details.
@ContextHierarchy
is a class-level annotation that is used to define a hierarchy of ApplicationContext
s for integration tests. @ContextHierarchy
should be declared with a list of one or more @ContextConfiguration
instances, each of which defines a level in the context hierarchy. The following examples demonstrate the use of @ContextHierarchy
within a single test class; however, @ContextHierarchy
can also be used within a test class hierarchy.
@ContextHierarchy({
@ContextConfiguration("/parent-config.xml"),
@ContextConfiguration("/child-config.xml")
})
public class ContextHierarchyTests {
// class body...
}
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = AppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
public class WebIntegrationTests {
// class body...
}
If you need to merge or override the configuration for a given level of the context hierarchy within a test class hierarchy, you must explicitly name that level by supplying the same value to the name
attribute in @ContextConfiguration
at each corresponding level in the class hierarchy. See the section called “Context hierarchies” and the @ContextHierarchy
javadocs for further examples.
@ActiveProfiles
is a class-level annotation that is used to declare which bean definition profiles should be active when loading an ApplicationContext
for an integration test.
@ContextConfiguration
@ActiveProfiles("dev")
public class DeveloperTests {
// class body...
}
@ContextConfiguration
@ActiveProfiles({"dev", "integration"})
public class DeveloperIntegrationTests {
// class body...
}
Note | |
---|---|
|
See the section called “Context configuration with environment profiles” and the @ActiveProfiles
javadocs for examples and further details.
@TestPropertySource
is a class-level annotation that is used to configure the locations of properties files and inlined properties to be added to the set of PropertySources
in the Environment
for an ApplicationContext
loaded for an integration test.
Test property sources have higher precedence than those loaded from the operating system’s environment or Java system properties as well as property sources added by the application declaratively via @PropertySource
or programmatically. Thus, test property sources can be used to selectively override properties defined in system and application property sources. Furthermore, inlined properties have higher precedence than properties loaded from resource locations.
The following example demonstrates how to declare a properties file from the classpath.
@ContextConfiguration
@TestPropertySource("/test.properties")
public class MyIntegrationTests {
// class body...
}
The following example demonstrates how to declare inlined properties.
@ContextConfiguration
@TestPropertySource(properties = { "timezone = GMT", "port: 4242" })
public class MyIntegrationTests {
// class body...
}
@DirtiesContext
indicates that the underlying Spring ApplicationContext
has been dirtied during the execution of a test (i.e., modified or corrupted in some manner — for example, by changing the state of a singleton bean) and should be closed. When an application context is marked dirty, it is removed from the testing framework’s cache and closed. As a consequence, the underlying Spring container will be rebuilt for any subsequent test that requires a context with the same configuration metadata.
@DirtiesContext
can be used as both a class-level and method-level annotation within the same class or class hierarchy. In such scenarios, the ApplicationContext
is marked as dirty before or after any such annotated method as well as before or after the current test class, depending on the configured methodMode
and classMode
.
The following examples explain when the context would be dirtied for various configuration scenarios:
Before the current test class, when declared on a class with class mode set to
BEFORE_CLASS
.@DirtiesContext(classMode = BEFORE_CLASS) public class FreshContextTests { // some tests that require a new Spring container }
After the current test class, when declared on a class with class mode set to
AFTER_CLASS
(i.e., the default class mode).@DirtiesContext public class ContextDirtyingTests { // some tests that result in the Spring container being dirtied }
Before each test method in the current test class, when declared on a class with class mode set to
BEFORE_EACH_TEST_METHOD.
@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD) public class FreshContextTests { // some tests that require a new Spring container }
After each test method in the current test class, when declared on a class with class mode set to
AFTER_EACH_TEST_METHOD.
@DirtiesContext(classMode = AFTER_EACH_TEST_METHOD) public class ContextDirtyingTests { // some tests that result in the Spring container being dirtied }
Before the current test, when declared on a method with the method mode set to
BEFORE_METHOD
.@DirtiesContext(methodMode = BEFORE_METHOD) @Test public void testProcessWhichRequiresFreshAppCtx() { // some logic that requires a new Spring container }
After the current test, when declared on a method with the method mode set to
AFTER_METHOD
(i.e., the default method mode).@DirtiesContext @Test public void testProcessWhichDirtiesAppCtx() { // some logic that results in the Spring container being dirtied }
If @DirtiesContext
is used in a test whose context is configured as part of a context hierarchy via @ContextHierarchy
, the hierarchyMode
flag can be used to control how the context cache is cleared. By default an exhaustive algorithm will be used that clears the context cache including not only the current level but also all other context hierarchies that share an ancestor context common to the current test; all ApplicationContext
s that reside in a sub-hierarchy of the common ancestor context will be removed from the context cache and closed. If the exhaustive algorithm is overkill for a particular use case, the simpler current level algorithm can be specified instead, as seen below.
@ContextHierarchy({
@ContextConfiguration("/parent-config.xml"),
@ContextConfiguration("/child-config.xml")
})
public class BaseTests {
// class body...
}
public class ExtendedTests extends BaseTests {
@Test
@DirtiesContext(hierarchyMode = CURRENT_LEVEL)
public void test() {
// some logic that results in the child context being dirtied
}
}
For further details regarding the EXHAUSTIVE
and CURRENT_LEVEL
algorithms see the DirtiesContext.HierarchyMode
javadocs.
@TestExecutionListeners
defines class-level metadata for configuring the TestExecutionListener
implementations that should be registered with the TestContextManager
. Typically, @TestExecutionListeners
is used in conjunction with @ContextConfiguration
.
@ContextConfiguration
@TestExecutionListeners({CustomTestExecutionListener.class, AnotherTestExecutionListener.class})
public class CustomTestExecutionListenerTests {
// class body...
}
@TestExecutionListeners
supports inherited listeners by default. See the javadocs for an example and further details.
@Commit
indicates that the transaction for a transactional test method should be committed after the test method has completed. @Commit
can be used as a direct replacement for @Rollback(false)
in order to more explicitly convey the intent of the code. Analogous to @Rollback
, @Commit
may also be declared as a class-level or method-level annotation.
@Commit
@Test
public void testProcessWithoutRollback() {
// ...
}
@Rollback
indicates whether the transaction for a transactional test method should be rolled back after the test method has completed. If true
, the transaction is rolled back; otherwise, the transaction is committed (see also @Commit
). Rollback semantics for integration tests in the Spring TestContext Framework default to true
even if @Rollback
is not explicitly declared.
When declared as a class-level annotation, @Rollback
defines the default rollback semantics for all test methods within the test class hierarchy. When declared as a method-level annotation, @Rollback
defines rollback semantics for the specific test method, potentially overriding class-level @Rollback
or @Commit
semantics.
@Rollback(false)
@Test
public void testProcessWithoutRollback() {
// ...
}
@BeforeTransaction
indicates that the annotated void
method should be executed before a transaction is started for test methods configured to run within a transaction via Spring’s @Transactional
annotation. As of Spring Framework 4.3, @BeforeTransaction
methods are not required to be public
and may be declared on Java 8 based interface default methods.
@BeforeTransaction
void beforeTransaction() {
// logic to be executed before a transaction is started
}
@AfterTransaction
indicates that the annotated void
method should be executed after a transaction is ended for test methods configured to run within a transaction via Spring’s @Transactional
annotation. As of Spring Framework 4.3, @AfterTransaction
methods are not required to be public
and may be declared on Java 8 based interface default methods.
@AfterTransaction
void afterTransaction() {
// logic to be executed after a transaction has ended
}
@Sql
is used to annotate a test class or test method to configure SQL scripts to be executed against a given database during integration tests.
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
public void userTest {
// execute code that relies on the test schema and test data
}
See the section called “Executing SQL scripts declaratively with @Sql” for further details.
@SqlConfig
defines metadata that is used to determine how to parse and execute SQL scripts configured via the @Sql
annotation.
@Test
@Sql(
scripts = "/test-user-data.sql",
config = @SqlConfig(commentPrefix = "`", separator = "@@")
)
public void userTest {
// execute code that relies on the test data
}
@SqlGroup
is a container annotation that aggregates several @Sql
annotations. @SqlGroup
can be used natively, declaring several nested @Sql
annotations, or it can be used in conjunction with Java 8’s support for repeatable annotations, where @Sql
can simply be declared several times on the same class or method, implicitly generating this container annotation.
@Test
@SqlGroup({
@Sql(scripts = "/test-schema.sql", config = @SqlConfig(commentPrefix = "`")),
@Sql("/test-user-data.sql")
)}
public void userTest {
// execute code that uses the test schema and test data
}
The following annotations are supported with standard semantics for all configurations of the Spring TestContext Framework. Note that these annotations are not specific to tests and can be used anywhere in the Spring Framework.
@Autowired
@Qualifier
@Resource
(javax.annotation) if JSR-250 is present@ManagedBean
(javax.annotation) if JSR-250 is present@Inject
(javax.inject) if JSR-330 is present@Named
(javax.inject) if JSR-330 is present@PersistenceContext
(javax.persistence) if JPA is present@PersistenceUnit
(javax.persistence) if JPA is present@Required
@Transactional
JSR-250 Lifecycle Annotations | |
---|---|
In the Spring TestContext Framework If a method within a test class is annotated with |
The following annotations are only supported when used in conjunction with the SpringRunner, Spring’s JUnit rules, or Spring’s JUnit 4 support classes.
@IfProfileValue
indicates that the annotated test is enabled for a specific testing environment. If the configured ProfileValueSource
returns a matching value
for the provided name
, the test is enabled. Otherwise, the test will be disabled and effectively ignored.
@IfProfileValue
can be applied at the class level, the method level, or both. Class-level usage of @IfProfileValue
takes precedence over method-level usage for any methods within that class or its subclasses. Specifically, a test is enabled if it is enabled both at the class level and at the method level; the absence of @IfProfileValue
means the test is implicitly enabled. This is analogous to the semantics of JUnit 4’s @Ignore
annotation, except that the presence of @Ignore
always disables a test.
@IfProfileValue(name="java.vendor", value="Oracle Corporation")
@Test
public void testProcessWhichRunsOnlyOnOracleJvm() {
// some logic that should run only on Java VMs from Oracle Corporation
}
Alternatively, you can configure @IfProfileValue
with a list of values
(with OR semantics) to achieve TestNG-like support for test groups in a JUnit 4 environment. Consider the following example:
@IfProfileValue(name="test-groups", values={"unit-tests", "integration-tests"})
@Test
public void testProcessWhichRunsForUnitOrIntegrationTestGroups() {
// some logic that should run only for unit and integration test groups
}
@ProfileValueSourceConfiguration
is a class-level annotation that specifies what type of ProfileValueSource
to use when retrieving profile values configured through the @IfProfileValue
annotation. If @ProfileValueSourceConfiguration
is not declared for a test, SystemProfileValueSource
is used by default.
@ProfileValueSourceConfiguration(CustomProfileValueSource.class)
public class CustomProfileValueSourceTests {
// class body...
}
@Timed
indicates that the annotated test method must finish execution in a specified time period (in milliseconds). If the text execution time exceeds the specified time period, the test fails.
The time period includes execution of the test method itself, any repetitions of the test (see @Repeat
), as well as any set up or tear down of the test fixture.
@Timed(millis=1000)
public void testProcessWithOneSecondTimeout() {
// some logic that should not take longer than 1 second to execute
}
Spring’s @Timed
annotation has different semantics than JUnit 4’s @Test(timeout=…)
support. Specifically, due to the manner in which JUnit 4 handles test execution timeouts (that is, by executing the test method in a separate Thread
), @Test(timeout=…)
preemptively fails the test if the test takes too long. Spring’s @Timed
, on the other hand, does not preemptively fail the test but rather waits for the test to complete before failing.
@Repeat
indicates that the annotated test method must be executed repeatedly. The number of times that the test method is to be executed is specified in the annotation.
The scope of execution to be repeated includes execution of the test method itself as well as any set up or tear down of the test fixture.
@Repeat(10)
@Test
public void testProcessRepeatedly() {
// ...
}
It is possible to use most test-related annotations as meta-annotations in order to create custom composed annotations and reduce configuration duplication across a test suite.
Each of the following may be used as meta-annotations in conjunction with the TestContext framework.
@BootstrapWith
@ContextConfiguration
@ContextHierarchy
@ActiveProfiles
@TestPropertySource
@DirtiesContext
@WebAppConfiguration
@TestExecutionListeners
@Transactional
@BeforeTransaction
@AfterTransaction
@Commit
@Rollback
@Sql
@SqlConfig
@SqlGroup
@Repeat
@Timed
@IfProfileValue
@ProfileValueSourceConfiguration
For example, if we discover that we are repeating the following configuration across our JUnit 4 based test suite…
@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class UserRepositoryTests { }
We can reduce the above duplication by introducing a custom composed annotation that centralizes the common test configuration like this:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public @interface TransactionalDevTest { }
Then we can use our custom @TransactionalDevTest
annotation to simplify the configuration of individual test classes as follows:
@RunWith(SpringRunner.class)
@TransactionalDevTest
public class OrderRepositoryTests { }
@RunWith(SpringRunner.class)
@TransactionalDevTest
public class UserRepositoryTests { }
For further details, consult the Spring Annotation Programming Model.
The Spring TestContext Framework (located in the org.springframework.test.context
package) provides generic, annotation-driven unit and integration testing support that is agnostic of the testing framework in use. The TestContext framework also places a great deal of importance on convention over configuration with reasonable defaults that can be overridden through annotation-based configuration.
In addition to generic testing infrastructure, the TestContext framework provides explicit support for JUnit 4 and TestNG in the form of abstract
support classes. For JUnit 4, Spring also provides a custom JUnit Runner
and custom JUnit Rules
that allow one to write so-called POJO test classes. POJO test classes are not required to extend a particular class hierarchy.
The following section provides an overview of the internals of the TestContext framework. If you are only interested in using the framework and not necessarily interested in extending it with your own custom listeners or custom loaders, feel free to go directly to the configuration (context management, dependency injection, transaction management), support classes, and annotation support sections.
The core of the framework consists of the TestContextManager
class and the TestContext
, TestExecutionListener
, and SmartContextLoader
interfaces. A TestContextManager
is created per test class (e.g., for the execution of all test methods within a single test class in JUnit 4). The TestContextManager
in turn manages a TestContext
that holds the context of the current test. The TestContextManager
also updates the state of the TestContext
as the test progresses and delegates to TestExecutionListener
implementations, which instrument the actual test execution by providing dependency injection, managing transactions, and so on. A SmartContextLoader
is responsible for loading an ApplicationContext
for a given test class. Consult the javadocs and the Spring test suite for further information and examples of various implementations.
TestContext
encapsulates the context in which a test is executed, agnostic of the actual testing framework in use, and provides context management and caching support for the test instance for which it is responsible. The TestContext
also delegates to a SmartContextLoader
to load an ApplicationContext
if requested.
TestContextManager
is the main entry point into the Spring TestContext Framework, which manages a single TestContext
and signals events to each registered TestExecutionListener
at well-defined test execution points:
- prior to any before class or before all methods of a particular testing framework
- test instance post-processing
- prior to any before or before each methods of a particular testing framework
- after any after or after each methods of a particular testing framework
- after any after class or after all methods of a particular testing framework
TestExecutionListener
defines the API for reacting to test execution events published by the TestContextManager
with which the listener is registered. See Section 15.5.3, “TestExecutionListener configuration”.
ContextLoader
is a strategy interface that was introduced in Spring 2.5 for loading an ApplicationContext
for an integration test managed by the Spring TestContext Framework. Implement SmartContextLoader
instead of this interface in order to provide support for annotated classes, active bean definition profiles, test property sources, context hierarchies, and WebApplicationContext
support.
SmartContextLoader
is an extension of the ContextLoader
interface introduced in Spring 3.1. The SmartContextLoader
SPI supersedes the ContextLoader
SPI that was introduced in Spring 2.5. Specifically, a SmartContextLoader
can choose to process resource locations
, annotated classes
, or context initializers
. Furthermore, a SmartContextLoader
can set active bean definition profiles and test property sources in the context that it loads.
Spring provides the following implementations:
DelegatingSmartContextLoader
: one of two default loaders which delegates internally to anAnnotationConfigContextLoader
, aGenericXmlContextLoader
, or aGenericGroovyXmlContextLoader
depending either on the configuration declared for the test class or on the presence of default locations or default configuration classes. Groovy support is only enabled if Groovy is on the classpath.WebDelegatingSmartContextLoader
: one of two default loaders which delegates internally to anAnnotationConfigWebContextLoader
, aGenericXmlWebContextLoader
, or aGenericGroovyXmlWebContextLoader
depending either on the configuration declared for the test class or on the presence of default locations or default configuration classes. A webContextLoader
will only be used if@WebAppConfiguration
is present on the test class. Groovy support is only enabled if Groovy is on the classpath.AnnotationConfigContextLoader
: loads a standardApplicationContext
from annotated classes.AnnotationConfigWebContextLoader
: loads aWebApplicationContext
from annotated classes.GenericGroovyXmlContextLoader
: loads a standardApplicationContext
from resource locations that are either Groovy scripts or XML configuration files.GenericGroovyXmlWebContextLoader
: loads aWebApplicationContext
from resource locations that are either Groovy scripts or XML configuration files.GenericXmlContextLoader
: loads a standardApplicationContext
from XML resource locations.GenericXmlWebContextLoader
: loads aWebApplicationContext
from XML resource locations.GenericPropertiesContextLoader
: loads a standardApplicationContext
from Java Properties files.
The default configuration for the internals of the Spring TestContext Framework is sufficient for all common use cases. However, there are times when a development team or third party framework would like to change the default ContextLoader
, implement a custom TestContext
or ContextCache
, augment the default sets of ContextCustomizerFactory
and TestExecutionListener
implementations, etc. For such low level control over how the TestContext framework operates, Spring provides a bootstrapping strategy.
TestContextBootstrapper
defines the SPI for bootstrapping the TestContext framework. A TestContextBootstrapper
is used by the TestContextManager
to load the TestExecutionListener
implementations for the current test and to build the TestContext
that it manages. A custom bootstrapping strategy can be configured for a test class (or test class hierarchy) via @BootstrapWith
, either directly or as a meta-annotation. If a bootstrapper is not explicitly configured via @BootstrapWith
, either the DefaultTestContextBootstrapper
or the WebTestContextBootstrapper
will be used, depending on the presence of @WebAppConfiguration
.
Since the TestContextBootstrapper
SPI is likely to change in the future in order to accommodate new requirements, implementers are strongly encouraged not to implement this interface directly but rather to extend AbstractTestContextBootstrapper
or one of its concrete subclasses instead.
Spring provides the following TestExecutionListener
implementations that are registered by default, exactly in this order.
ServletTestExecutionListener
: configures Servlet API mocks for aWebApplicationContext
DirtiesContextBeforeModesTestExecutionListener
: handles the@DirtiesContext
annotation for before modesDependencyInjectionTestExecutionListener
: provides dependency injection for the test instanceDirtiesContextTestExecutionListener
: handles the@DirtiesContext
annotation for after modesTransactionalTestExecutionListener
: provides transactional test execution with default rollback semanticsSqlScriptsTestExecutionListener
: executes SQL scripts configured via the@Sql
annotation
Custom TestExecutionListener
s can be registered for a test class and its subclasses via the @TestExecutionListeners
annotation. See annotation support and the javadocs for @TestExecutionListeners
for details and examples.
Registering custom TestExecutionListener
s via @TestExecutionListeners
is suitable for custom listeners that are used in limited testing scenarios; however, it can become cumbersome if a custom listener needs to be used across a test suite. Since Spring Framework 4.1, this issue is addressed via support for automatic discovery of default TestExecutionListener
implementations via the SpringFactoriesLoader
mechanism.
Specifically, the spring-test
module declares all core default TestExecutionListener
s under the org.springframework.test.context.TestExecutionListener
key in its META-INF/spring.factories
properties file. Third-party frameworks and developers can contribute their own TestExecutionListener
s to the list of default listeners in the same manner via their own META-INF/spring.factories
properties file.
When the TestContext framework discovers default TestExecutionListener
s via the aforementioned SpringFactoriesLoader
mechanism, the instantiated listeners are sorted using Spring’s AnnotationAwareOrderComparator
which honors Spring’s Ordered
interface and @Order
annotation for ordering. AbstractTestExecutionListener
and all default TestExecutionListener
s provided by Spring implement Ordered
with appropriate values. Third-party frameworks and developers should therefore make sure that their default TestExecutionListener
s are registered in the proper order by implementing Ordered
or declaring @Order
. Consult the javadocs for the getOrder()
methods of the core default TestExecutionListener
s for details on what values are assigned to each core listener.
If a custom TestExecutionListener
is registered via @TestExecutionListeners
, the default listeners will not be registered. In most common testing scenarios, this effectively forces the developer to manually declare all default listeners in addition to any custom listeners. The following listing demonstrates this style of configuration.
@ContextConfiguration
@TestExecutionListeners({
MyCustomTestExecutionListener.class,
ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class
})
public class MyTest {
// class body...
}
The challenge with this approach is that it requires that the developer know exactly which listeners are registered by default. Moreover, the set of default listeners can change from release to release — for example, SqlScriptsTestExecutionListener
was introduced in Spring Framework 4.1, and DirtiesContextBeforeModesTestExecutionListener
was introduced in Spring Framework 4.2. Furthermore, third-party frameworks like Spring Security register their own default TestExecutionListener
s via the aforementioned automatic discovery mechanism.
To avoid having to be aware of and re-declare all default listeners, the mergeMode
attribute of @TestExecutionListeners
can be set to MergeMode.MERGE_WITH_DEFAULTS
. MERGE_WITH_DEFAULTS
indicates that locally declared listeners should be merged with the default listeners. The merging algorithm ensures that duplicates are removed from the list and that the resulting set of merged listeners is sorted according to the semantics of AnnotationAwareOrderComparator
as described in the section called “Ordering TestExecutionListeners”. If a listener implements Ordered
or is annotated with @Order
it can influence the position in which it is merged with the defaults; otherwise, locally declared listeners will simply be appended to the list of default listeners when merged.
For example, if the MyCustomTestExecutionListener
class in the previous example configures its order
value (for example, 500
) to be less than the order of the ServletTestExecutionListener
(which happens to be 1000
), the MyCustomTestExecutionListener
can then be automatically merged with the list of defaults in front of the ServletTestExecutionListener
, and the previous example could be replaced with the following.
@ContextConfiguration
@TestExecutionListeners(
listeners = MyCustomTestExecutionListener.class,
mergeMode = MERGE_WITH_DEFAULTS
)
public class MyTest {
// class body...
}
Each TestContext
provides context management and caching support for the test instance it is responsible for. Test instances do not automatically receive access to the configured ApplicationContext
. However, if a test class implements the ApplicationContextAware
interface, a reference to the ApplicationContext
is supplied to the test instance. Note that AbstractJUnit4SpringContextTests
and AbstractTestNGSpringContextTests
implement ApplicationContextAware
and therefore provide access to the ApplicationContext
automatically.
@Autowired ApplicationContext | |
---|---|
As an alternative to implementing the Similarly, if your test is configured to load a Dependency injection via |
Test classes that use the TestContext framework do not need to extend any particular class or implement a specific interface to configure their application context. Instead, configuration is achieved simply by declaring the @ContextConfiguration
annotation at the class level. If your test class does not explicitly declare application context resource locations
or annotated classes
, the configured ContextLoader
determines how to load a context from a default location or default configuration classes. In addition to context resource locations
and annotated classes
, an application context can also be configured via application context initializers
.
The following sections explain how to configure an ApplicationContext
via XML configuration files, Groovy scripts, annotated classes (typically @Configuration
classes), or context initializers using Spring’s @ContextConfiguration
annotation. Alternatively, you can implement and configure your own custom SmartContextLoader
for advanced use cases.
To load an ApplicationContext
for your tests using XML configuration files, annotate your test class with @ContextConfiguration
and configure the locations
attribute with an array that contains the resource locations of XML configuration metadata. A plain or relative path — for example "context.xml"
— will be treated as a classpath resource that is relative to the package in which the test class is defined. A path starting with a slash is treated as an absolute classpath location, for example "/org/example/config.xml"
. A path which represents a resource URL (i.e., a path prefixed with classpath:
, file:
, http:
, etc.) will be used as is.
@RunWith(SpringRunner.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"})
public class MyTest {
// class body...
}
@ContextConfiguration
supports an alias for the locations
attribute through the standard Java value
attribute. Thus, if you do not need to declare additional attributes in @ContextConfiguration
, you can omit the declaration of the locations
attribute name and declare the resource locations by using the shorthand format demonstrated in the following example.
@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-config.xml"})
public class MyTest {
// class body...
}
If you omit both the locations
and value
attributes from the @ContextConfiguration
annotation, the TestContext framework will attempt to detect a default XML resource location. Specifically, GenericXmlContextLoader
and GenericXmlWebContextLoader
detect a default location based on the name of the test class. If your class is named com.example.MyTest
, GenericXmlContextLoader
loads your application context from "classpath:com/example/MyTest-context.xml"
.
package com.example;
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTest-context.xml"
@ContextConfiguration
public class MyTest {
// class body...
}
To load an ApplicationContext
for your tests using Groovy scripts that utilize the Groovy Bean Definition DSL, annotate your test class with @ContextConfiguration
and configure the locations
or value
attribute with an array that contains the resource locations of Groovy scripts. Resource lookup semantics for Groovy scripts are the same as those described for XML configuration files.
Enabling Groovy script support | |
---|---|
Support for using Groovy scripts to load an |
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from "/AppConfig.groovy" and
// "/TestConfig.groovy" in the root of the classpath
@ContextConfiguration({"/AppConfig.groovy", "/TestConfig.Groovy"})
public class MyTest {
// class body...
}
If you omit both the locations
and value
attributes from the @ContextConfiguration
annotation, the TestContext framework will attempt to detect a default Groovy script. Specifically, GenericGroovyXmlContextLoader
and GenericGroovyXmlWebContextLoader
detect 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"
.
package com.example;
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from
// "classpath:com/example/MyTestContext.groovy"
@ContextConfiguration
public class MyTest {
// class body...
}
Declaring XML config and Groovy scripts simultaneously | |
---|---|
Both XML configuration files and Groovy scripts can be declared simultaneously via the The following listing demonstrates how to combine both in an integration test.
|
To load an ApplicationContext
for your tests using annotated classes (see Section 7.12, “Java-based container configuration”), annotate your test class with @ContextConfiguration
and configure the classes
attribute with an array that contains references to annotated classes.
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from AppConfig and TestConfig
@ContextConfiguration(classes = {AppConfig.class, TestConfig.class})
public class MyTest {
// class body...
}
Annotated Classes | |
---|---|
The term annotated class can refer to any of the following.
Consult the javadocs of |
If you omit the classes
attribute from the @ContextConfiguration
annotation, the TestContext framework will attempt to detect the presence of default configuration classes. Specifically, AnnotationConfigContextLoader
and AnnotationConfigWebContextLoader
will detect all static
nested classes of the test class that meet the requirements for configuration class implementations as specified in the @Configuration
javadocs. In the following example, the OrderServiceTest
class declares a static
nested configuration class named Config
that will be automatically used to load the ApplicationContext
for the test class. Note that the name of the configuration class is arbitrary. In addition, a test class can contain more than one static
nested configuration class if desired.
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from the
// static nested Config class
@ContextConfiguration
public class OrderServiceTest {
@Configuration
static class Config {
// this bean will be injected into the OrderServiceTest class
@Bean
public OrderService orderService() {
OrderService orderService = new OrderServiceImpl();
// set properties, etc.
return orderService;
}
}
@Autowired
private OrderService orderService;
@Test
public void testOrderService() {
// test the orderService
}
}
It may sometimes be desirable to mix XML configuration files, Groovy scripts, and annotated classes (i.e., typically @Configuration
classes) to configure an ApplicationContext
for your tests. For example, if you use XML configuration in production, you may decide that you want to use @Configuration
classes to configure specific Spring-managed components for your tests, or vice versa.
Furthermore, some third-party frameworks (like Spring Boot) provide first-class support for loading an ApplicationContext
from different types of resources simultaneously (e.g., XML configuration files, Groovy scripts, and @Configuration
classes). The Spring Framework historically has not supported this for standard deployments. Consequently, most of the SmartContextLoader
implementations that the Spring Framework delivers in the spring-test
module support only one resource type per test context; however, this does not mean that you cannot use both. One exception to the general rule is that the GenericGroovyXmlContextLoader
and GenericGroovyXmlWebContextLoader
support both XML configuration files and Groovy scripts simultaneously. Furthermore, third-party frameworks may choose to support the declaration of both locations
and classes
via @ContextConfiguration
, and with the standard testing support in the TestContext framework, you have the following options.
If you want to use resource locations (e.g., XML or Groovy) and @Configuration
classes to configure your tests, you will have to pick one as the entry point, and that one will have to include or import the other. For example, in XML or Groovy scripts you can include @Configuration
classes via component scanning or define them as normal Spring beans; whereas, in a @Configuration
class you can use @ImportResource
to import XML configuration files or Groovy scripts. Note that this behavior is semantically equivalent to how you configure your application in production: in production configuration you will define either a set of XML or Groovy resource locations or a set of @Configuration
classes that your production ApplicationContext
will be loaded from, but you still have the freedom to include or import the other type of configuration.
To configure an ApplicationContext
for your tests using context initializers, annotate your test class with @ContextConfiguration
and configure the initializers
attribute with an array that contains references to classes that implement ApplicationContextInitializer
. The declared context initializers will then be used to initialize the ConfigurableApplicationContext
that is loaded for your tests. Note that the concrete ConfigurableApplicationContext
type supported by each declared initializer must be compatible with the type of ApplicationContext
created by the SmartContextLoader
in use (i.e., typically a GenericApplicationContext
). Furthermore, the order in which the initializers are invoked depends on whether they implement Spring’s Ordered
interface or are annotated with Spring’s @Order
annotation or the standard @Priority
annotation.
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from TestConfig
// and initialized by TestAppCtxInitializer
@ContextConfiguration(
classes = TestConfig.class,
initializers = TestAppCtxInitializer.class)
public class MyTest {
// class body...
}
It is also possible to omit the declaration of XML configuration files, Groovy scripts, or annotated classes in @ContextConfiguration
entirely and instead declare only ApplicationContextInitializer
classes which are then responsible for registering beans in the context — for example, by programmatically loading bean definitions from XML files or configuration classes.
@RunWith(SpringRunner.class)
// ApplicationContext will be initialized by EntireAppInitializer
// which presumably registers beans in the context
@ContextConfiguration(initializers = EntireAppInitializer.class)
public class MyTest {
// class body...
}
@ContextConfiguration
supports boolean inheritLocations
and inheritInitializers
attributes that denote whether resource locations or annotated classes and context initializers declared by superclasses should be inherited. The default value for both flags is true
. This means that a test class inherits the resource locations or annotated classes as well as the context initializers declared by any superclasses. Specifically, the resource locations or annotated classes for a test class are appended to the list of resource locations or annotated classes declared by superclasses. Similarly, the initializers for a given test class will be added to the set of initializers defined by test superclasses. Thus, subclasses have the option of extending the resource locations, annotated classes, or context initializers.
If the inheritLocations
or inheritInitializers
attribute in @ContextConfiguration
is set to false
, the resource locations or annotated classes and the context initializers, respectively, for the test class shadow and effectively replace the configuration defined by superclasses.
In the following example that uses XML resource locations, the ApplicationContext
for ExtendedTest
will be loaded from "base-config.xml" and "extended-config.xml", in that order. Beans defined in "extended-config.xml" may therefore override (i.e., replace) those defined in "base-config.xml".
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from "/base-config.xml"
// in the root of the classpath
@ContextConfiguration("/base-config.xml")
public class BaseTest {
// class body...
}
// ApplicationContext will be loaded from "/base-config.xml" and
// "/extended-config.xml" in the root of the classpath
@ContextConfiguration("/extended-config.xml")
public class ExtendedTest extends BaseTest {
// class body...
}
Similarly, in the following example that uses annotated classes, the ApplicationContext
for ExtendedTest
will be loaded from the BaseConfig
and ExtendedConfig
classes, in that order. Beans defined in ExtendedConfig
may therefore override (i.e., replace) those defined in BaseConfig
.
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from BaseConfig
@ContextConfiguration(classes = BaseConfig.class)
public class BaseTest {
// class body...
}
// ApplicationContext will be loaded from BaseConfig and ExtendedConfig
@ContextConfiguration(classes = ExtendedConfig.class)
public class ExtendedTest extends BaseTest {
// class body...
}
In the following example that uses context initializers, the ApplicationContext
for ExtendedTest
will be initialized using BaseInitializer
and ExtendedInitializer
. Note, however, that the order in which the initializers are invoked depends on whether they implement Spring’s Ordered
interface or are annotated with Spring’s @Order
annotation or the standard @Priority
annotation.
@RunWith(SpringRunner.class)
// ApplicationContext will be initialized by BaseInitializer
@ContextConfiguration(initializers = BaseInitializer.class)
public class BaseTest {
// class body...
}
// ApplicationContext will be initialized by BaseInitializer
// and ExtendedInitializer
@ContextConfiguration(initializers = ExtendedInitializer.class)
public class ExtendedTest extends BaseTest {
// class body...
}
Spring 3.1 introduced first-class support in the framework for the notion of environments and profiles (a.k.a., bean definition profiles), and integration tests can be configured to activate particular bean definition profiles for various testing scenarios. This is achieved by annotating a test class with the @ActiveProfiles
annotation and supplying a list of profiles that should be activated when loading the ApplicationContext
for the test.
Note | |
---|---|
|
Let’s take a look at some examples with XML configuration and @Configuration
classes.
<!-- 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>
package com.bank.service;
@RunWith(SpringRunner.class)
// ApplicationContext will be loaded from "classpath:/app-config.xml"
@ContextConfiguration("/app-config.xml")
@ActiveProfiles("dev")
public class TransferServiceTest {
@Autowired
private TransferService transferService;
@Test
public void testTransferService() {
// test the transferService
}
}
When TransferServiceTest
is run, its ApplicationContext
will be loaded from the app-config.xml
configuration file in the root of the classpath. If you inspect app-config.xml
you’ll notice that the accountRepository
bean has a dependency on a dataSource
bean; however, dataSource
is not defined as a top-level bean. Instead, dataSource
is defined three times: in the production profile, the dev profile, and the default profile.
By annotating TransferServiceTest
with @ActiveProfiles("dev")
we instruct the Spring TestContext Framework to load the ApplicationContext
with the active profiles set to {"dev"}
. As a result, an embedded database will be created and populated with test data, and the accountRepository
bean will be wired with a reference to the development DataSource
. And that’s likely what we want in an integration test.
It is sometimes useful to assign beans to a default
profile. Beans within the default profile are only included when no other profile is specifically activated. This can be used to define fallback beans to be used in the application’s default state. For example, you may explicitly provide a data source for dev
and production
profiles, but define an in-memory data source as a default when neither of these is active.
The following code listings demonstrate how to implement the same configuration and integration test but using @Configuration
classes 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("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("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new 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();
}
}
package com.bank.service;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
public class TransferServiceTest {
@Autowired
private TransferService transferService;
@Test
public void testTransferService() {
// test the transferService
}
}
In this variation, we have split the XML configuration into four independent @Configuration
classes:
TransferServiceConfig
: acquires adataSource
via dependency injection using@Autowired
StandaloneDataConfig
: defines adataSource
for an embedded database suitable for developer testsJndiDataConfig
: defines adataSource
that is retrieved from JNDI in a production environmentDefaultDataConfig
: defines adataSource
for a default embedded database in case no profile is active
As with the XML-based configuration example, we still annotate TransferServiceTest
with @ActiveProfiles("dev")
, but this time we specify all four configuration classes via the @ContextConfiguration
annotation. The body of the test class itself remains completely unchanged.
It is often the case that a single set of profiles is used across multiple test classes within a given project. Thus, to avoid duplicate declarations of the @ActiveProfiles
annotation it is possible to declare @ActiveProfiles
once on a base class, and subclasses will automatically inherit the @ActiveProfiles
configuration from the base class. In the following example, the declaration of @ActiveProfiles
(as well as other annotations) has been moved to an abstract superclass, AbstractIntegrationTest
.
package com.bank.service;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {
TransferServiceConfig.class,
StandaloneDataConfig.class,
JndiDataConfig.class,
DefaultDataConfig.class})
@ActiveProfiles("dev")
public abstract class AbstractIntegrationTest {
}
package com.bank.service;
// "dev" profile inherited from superclass
public class TransferServiceTest extends AbstractIntegrationTest {
@Autowired
private TransferService transferService;
@Test
public void testTransferService() {
// test the transferService
}
}
@ActiveProfiles
also supports an inheritProfiles
attribute that can be used to disable the inheritance of active profiles.
package com.bank.service;
// "dev" profile overridden with "production"
@ActiveProfiles(profiles = "production", inheritProfiles = false)
public class ProductionTransferServiceTest extends AbstractIntegrationTest {
// test body
}
Furthermore, it is sometimes necessary to resolve active profiles for tests programmatically instead of declaratively — for example, based on:
- the current operating system
- whether tests are being executed on a continuous integration build server
- the presence of certain environment variables
- the presence of custom class-level annotations
- etc.
To resolve active bean definition profiles programmatically, simply implement a custom ActiveProfilesResolver
and register it via the resolver
attribute of @ActiveProfiles
. The following example demonstrates how to implement and register a custom OperatingSystemActiveProfilesResolver
. For further information, refer to the corresponding javadocs.
package com.bank.service;
// "dev" profile overridden programmatically via a custom resolver
@ActiveProfiles(
resolver = OperatingSystemActiveProfilesResolver.class,
inheritProfiles = false)
public class TransferServiceTest extends AbstractIntegrationTest {
// test body
}
package com.bank.service.test;
public class OperatingSystemActiveProfilesResolver implements ActiveProfilesResolver {
@Override
String[] resolve(Class<?> testClass) {
String profile = ...;
// determine the value of profile based on the operating system
return new String[] {profile};
}
}
Spring 3.1 introduced first-class support in the framework for the notion of an environment with a hierarchy of property sources, and since Spring 4.1 integration tests can be configured with test-specific property sources. In contrast to the @PropertySource
annotation used on @Configuration
classes, the @TestPropertySource
annotation can be declared on a test class to declare resource locations for test properties files or inlined properties. These test property sources will be added to the set of PropertySources
in the Environment
for the ApplicationContext
loaded for the annotated integration test.
Note | |
---|---|
Implementations of |
Declaring test property sources
Test properties files can be configured via the locations
or value
attribute of @TestPropertySource
as shown in the following example.
Both traditional and XML-based properties file formats are supported — for example, "classpath:/com/example/test.properties"
or "file:///path/to/file.xml"
.
Each path will be interpreted as a Spring Resource
. A plain path — for example, "test.properties"
— will be treated as a classpath resource that is relative to the package in which the test class is defined. A path starting with a slash will be treated as an absolute classpath resource, for example: "/org/example/test.xml"
. A path which references a URL (e.g., a path prefixed with classpath:
, file:
, http:
, etc.) will be loaded using the specified resource protocol. Resource location wildcards (e.g. */.properties
) are not permitted: each location must evaluate to exactly one .properties
or .xml
resource.
@ContextConfiguration
@TestPropertySource("/test.properties")
public class MyIntegrationTests {
// class body...
}
Inlined properties in the form of key-value pairs can be configured via the properties
attribute of @TestPropertySource
as shown in the following example. All key-value pairs will be added to the enclosing Environment
as a single test PropertySource
with the highest precedence.
The supported syntax for key-value pairs is the same as the syntax defined for entries in a Java properties file:
"key=value"
"key:value"
"key value"
@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"})
public class MyIntegrationTests {
// class body...
}
Default properties file detection
If @TestPropertySource
is declared as an empty annotation (i.e., without explicit values for the locations
or properties
attributes), an attempt will be made to detect a default properties file relative to the class that declared the annotation. For example, if the annotated test class is com.example.MyTest
, the corresponding default properties file is "classpath:com/example/MyTest.properties"
. If the default cannot be detected, an IllegalStateException
will be thrown.
Precedence
Test property sources have higher precedence than those loaded from the operating system’s environment or Java system properties as well as property sources added by the application declaratively via @PropertySource
or programmatically. Thus, test property sources can be used to selectively override properties defined in system and application property sources. Furthermore, inlined properties have higher precedence than properties loaded from resource locations.
In the following example, the timezone
and port
properties as well as any properties defined in "/test.properties"
will override any properties of the same name that are defined in system and application property sources. Furthermore, if the "/test.properties"
file defines entries for the timezone
and port
properties those will be overridden by the inlined properties declared via the properties
attribute.
@ContextConfiguration
@TestPropertySource(
locations = "/test.properties",
properties = {"timezone = GMT", "port: 4242"}
)
public class MyIntegrationTests {
// class body...
}
Inheriting and overriding test property sources
@TestPropertySource
supports boolean inheritLocations
and inheritProperties
attributes that denote whether resource locations for properties files and inlined properties declared by superclasses should be inherited. The default value for both flags is true
. This means that a test class inherits the locations and inlined properties declared by any superclasses. Specifically, the locations and inlined properties for a test class are appended to the locations and inlined properties declared by superclasses. Thus, subclasses have the option of extending the locations and inlined properties. Note that properties that appear later will shadow (i.e.., override) properties of the same name that appear earlier. In addition, the aforementioned precedence rules apply for inherited test property sources as well.
If the inheritLocations
or inheritProperties
attribute in @TestPropertySource
is set to false
, the locations or inlined properties, respectively, for the test class shadow and effectively replace the configuration defined by superclasses.
In the following example, the ApplicationContext
for BaseTest
will be loaded using only the "base.properties"
file as a test property source. In contrast, the ApplicationContext
for ExtendedTest
will be loaded using the "base.properties"
and "extended.properties"
files as test property source locations.
@TestPropertySource("base.properties")
@ContextConfiguration
public class BaseTest {
// ...
}
@TestPropertySource("extended.properties")
@ContextConfiguration
public class ExtendedTest extends BaseTest {
// ...
}
In the following example, the ApplicationContext
for BaseTest
will be loaded using only the inlined key1
property. In contrast, the ApplicationContext
for ExtendedTest
will be loaded using the inlined key1
and key2
properties.
@TestPropertySource(properties = "key1 = value1")
@ContextConfiguration
public class BaseTest {
// ...
}
@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
public class ExtendedTest extends BaseTest {
// ...
}
Spring 3.2 introduced support for loading a WebApplicationContext
in integration tests. To instruct the TestContext framework to load a WebApplicationContext
instead of a standard ApplicationContext
, simply annotate the respective test class with @WebAppConfiguration
.
The presence of @WebAppConfiguration
on your test class instructs the TestContext framework (TCF) that a WebApplicationContext
(WAC) should be loaded for your integration tests. In the background the TCF makes sure that a MockServletContext
is created and supplied to your test’s WAC. By default the base resource path for your MockServletContext
will be set to "src/main/webapp". This is interpreted as a path relative to the root of your JVM (i.e., normally the path to your project). If you’re familiar with the directory structure of a web application in a Maven project, you’ll know that "src/main/webapp" is the default location for the root of your WAR. If you need to override this default, simply provide an alternate path to the @WebAppConfiguration
annotation (e.g., @WebAppConfiguration("src/test/webapp")
). If you wish to reference a base resource path from the classpath instead of the file system, just use Spring’s classpath: prefix.
Please note that Spring’s testing support for WebApplicationContexts
is on par with its support for standard ApplicationContexts
. When testing with a WebApplicationContext
you are free to declare XML configuration files, Groovy scripts, or @Configuration
classes via @ContextConfiguration
. You are of course also free to use any other test annotations such as @ActiveProfiles
, @TestExecutionListeners
, @Sql
, @Rollback
, etc.
The following examples demonstrate some of the various configuration options for loading a WebApplicationContext
.
Conventions.
@RunWith(SpringRunner.class)
// defaults to "file:src/main/webapp"
@WebAppConfiguration
// detects "WacTests-context.xml" in same package
// or static nested @Configuration class
@ContextConfiguration
public class WacTests {
//...
}
The above example demonstrates the TestContext framework’s support for convention over configuration. If you annotate a test class with @WebAppConfiguration
without specifying a resource base path, the resource path will effectively default to "file:src/main/webapp". Similarly, if you declare @ContextConfiguration
without specifying resource locations
, annotated classes
, or context initializers
, Spring will attempt to detect the presence of your configuration using conventions (i.e., "WacTests-context.xml" in the same package as the WacTests
class or static nested @Configuration
classes).
Default resource semantics.
@RunWith(SpringRunner.class)
// file system resource
@WebAppConfiguration("webapp")
// classpath resource
@ContextConfiguration("/spring/test-servlet-config.xml")
public class WacTests {
//...
}
This example demonstrates how to explicitly declare a resource base path with @WebAppConfiguration
and an XML resource location with @ContextConfiguration
. The important thing to note here is the different semantics for paths with these two annotations. By default, @WebAppConfiguration
resource paths are file system based; whereas, @ContextConfiguration
resource locations are classpath based.
Explicit resource semantics.
@RunWith(SpringRunner.class)
// classpath resource
@WebAppConfiguration("classpath:test-web-resources")
// file system resource
@ContextConfiguration("file:src/main/webapp/WEB-INF/servlet-config.xml")
public class WacTests {
//...
}
In this third example, we see that we can override the default resource semantics for both annotations by specifying a Spring resource prefix. Contrast the comments in this example with the previous example.
To provide comprehensive web testing support, Spring 3.2 introduced a ServletTestExecutionListener
that is enabled by default. When testing against a WebApplicationContext
this TestExecutionListener sets up default thread-local state via Spring Web’s RequestContextHolder
before each test method and creates a MockHttpServletRequest
, MockHttpServletResponse
, and ServletWebRequest
based on the base resource path configured via @WebAppConfiguration
. ServletTestExecutionListener
also ensures that the MockHttpServletResponse
and ServletWebRequest
can be injected into the test instance, and once the test is complete it cleans up thread-local state.
Once you have a WebApplicationContext
loaded for your test you might find that you need to interact with the web mocks — for example, to set up your test fixture or to perform assertions after invoking your web component. The following example demonstrates which mocks can be autowired into your test instance. Note that the WebApplicationContext
and MockServletContext
are both cached across the test suite; whereas, the other mocks are managed per test method by the ServletTestExecutionListener
.
Injecting mocks.
@WebAppConfiguration
@ContextConfiguration
public class WacTests {
@Autowired
WebApplicationContext wac; // cached
@Autowired
MockServletContext servletContext; // cached
@Autowired
MockHttpSession session;
@Autowired
MockHttpServletRequest request;
@Autowired
MockHttpServletResponse response;
@Autowired
ServletWebRequest webRequest;
//...
}
Once the TestContext framework loads an ApplicationContext
(or WebApplicationContext
) for a test, that context will be 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.
An ApplicationContext
can be uniquely identified by the combination of configuration parameters that are used to load it. Consequently, the unique combination of configuration parameters are used to generate a key under which the context is cached. The TestContext framework uses the following configuration parameters to build the context cache key:
locations
(from @ContextConfiguration)classes
(from @ContextConfiguration)contextInitializerClasses
(from @ContextConfiguration)contextCustomizers
(from ContextCustomizerFactory)contextLoader
(from @ContextConfiguration)parent
(from @ContextHierarchy)activeProfiles
(from @ActiveProfiles)propertySourceLocations
(from @TestPropertySource)propertySourceProperties
(from @TestPropertySource)resourceBasePath
(from @WebAppConfiguration)
For example, if TestClassA
specifies {"app-config.xml", "test-config.xml"}
for the locations
(or value
) attribute of @ContextConfiguration
, the TestContext framework will load the corresponding ApplicationContext
and store it in a static
context cache under a key that is based solely on those locations. So if TestClassB
also defines {"app-config.xml", "test-config.xml"}
for its locations (either explicitly or implicitly through inheritance) but does not define @WebAppConfiguration
, a different ContextLoader
, different active profiles, different context initializers, different test property sources, or a different parent context, then the same ApplicationContext
will be shared by both test classes. This means that the setup cost for loading an application context is incurred only once (per test suite), and subsequent test execution is much faster.
Test suites and forked processes | |
---|---|
The Spring TestContext framework stores application contexts in a static cache. This means that the context is literally stored in a To benefit from the caching mechanism, all tests must run within the same process or test suite. This can be achieved by executing all tests as a group within an IDE. Similarly, when executing tests with a build framework such as Ant, Maven, or Gradle it is important to make sure that the build framework does not fork between tests. For example, if the forkMode for the Maven Surefire plug-in is set to |
Since Spring Framework 4.3, the size of the context cache is bounded with a default maximum size of 32. Whenever the maximum size is reached, a least recently used (LRU) eviction policy is used to evict and close stale contexts. The maximum size can be configured from the command line or a build script by setting a JVM system property named spring.test.context.cache.maxSize
. As an alternative, the same property can be set programmatically via the SpringProperties
API.
Since having a large number of application contexts loaded within a given test suite can cause the suite to take an unnecessarily long time to execute, it is often beneficial to know exactly how many contexts have been loaded and cached. To view the statistics for the underlying context cache, simply set the log level for the org.springframework.test.context.cache
logging category to DEBUG
.
In the unlikely case that a test corrupts the application context and requires reloading — for example, by modifying a bean definition or the state of an application object — you can annotate your test class or test method with @DirtiesContext
(see the discussion of @DirtiesContext
in Section 15.4.1, “Spring Testing Annotations”). This instructs Spring to remove the context from the cache and rebuild the application context before executing the next test. Note that support for the @DirtiesContext
annotation is provided by the DirtiesContextBeforeModesTestExecutionListener
and the DirtiesContextTestExecutionListener
which are enabled by default.
When writing integration tests that rely on a loaded Spring ApplicationContext
, it is often sufficient to test against a single context; however, there are times when it is beneficial or even necessary to test against a hierarchy of ApplicationContext
s. For example, if you are developing a Spring MVC web application you will typically have a root WebApplicationContext
loaded via Spring’s ContextLoaderListener
and a child WebApplicationContext
loaded via Spring’s DispatcherServlet
. This results in a parent-child context hierarchy where shared components and infrastructure configuration are declared in the root context and consumed in the child context by web-specific components. Another use case can be found in Spring Batch applications where you often have a parent context that provides configuration for shared batch infrastructure and a child context for the configuration of a specific batch job.
Since Spring Framework 3.2.2, it is possible to write integration tests that use context hierarchies by declaring context configuration via the @ContextHierarchy
annotation, either on an individual test class or within a test class hierarchy. If a context hierarchy is declared on multiple classes within a test class hierarchy it is also possible to merge or override the context configuration for a specific, named level in the context hierarchy. When merging configuration for a given level in the hierarchy the configuration resource type (i.e., XML configuration files or annotated classes) must be consistent; otherwise, it is perfectly acceptable to have different levels in a context hierarchy configured using different resource types.
The following JUnit 4 based examples demonstrate common configuration scenarios for integration tests that require the use of context hierarchies.
ControllerIntegrationTests
represents a typical integration testing scenario for a Spring MVC web application by declaring a context hierarchy consisting of two levels, one for the root WebApplicationContext (loaded using the TestAppConfig
@Configuration
class) and one for the dispatcher servlet WebApplicationContext
(loaded using the WebConfig
@Configuration
class). The WebApplicationContext
that is autowired into the test instance is the one for the child context (i.e., the lowest context in the hierarchy).
@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = TestAppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
public class ControllerIntegrationTests {
@Autowired
private WebApplicationContext wac;
// ...
}
The following test classes define a context hierarchy within a test class hierarchy. AbstractWebTests
declares the configuration for a root WebApplicationContext
in a Spring-powered web application. Note, however, that AbstractWebTests
does not declare @ContextHierarchy
; consequently, subclasses of AbstractWebTests
can optionally participate in a context hierarchy or simply follow the standard semantics for @ContextConfiguration
. SoapWebServiceTests
and RestWebServiceTests
both extend AbstractWebTests
and define a context hierarchy via @ContextHierarchy
. The result is that three application contexts will be loaded (one for each declaration of @ContextConfiguration
), and the application context loaded based on the configuration in AbstractWebTests
will be set as the parent context for each of the contexts loaded for the concrete subclasses.
@RunWith(SpringRunner.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 {}
The following classes demonstrate the use of named hierarchy levels in order to merge the configuration for specific levels in a context hierarchy. BaseTests
defines two levels in the hierarchy, parent
and child
. ExtendedTests
extends BaseTests
and instructs the Spring TestContext Framework to merge the context configuration for the child
hierarchy level, simply by ensuring that the names declared via the name
attribute in @ContextConfiguration
are both "child"
. The result is that three application contexts will be loaded: one for "/app-config.xml"
, one for "/user-config.xml"
, and one for {"/user-config.xml", "/order-config.xml"}
. As with the previous example, the application context loaded from "/app-config.xml"
will be set as the parent context for the contexts loaded from "/user-config.xml"
and {"/user-config.xml", "/order-config.xml"}
.
@RunWith(SpringRunner.class)
@ContextHierarchy({
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
@ContextConfiguration(name = "child", locations = "/user-config.xml")
})
public class BaseTests {}
@ContextHierarchy(
@ContextConfiguration(name = "child", locations = "/order-config.xml")
)
public class ExtendedTests extends BaseTests {}
In contrast to the previous example, this example demonstrates how to override the configuration for a given named level in a context hierarchy by setting the inheritLocations
flag in @ContextConfiguration
to false
. Consequently, the application context for ExtendedTests
will be loaded only from "/test-user-config.xml"
and will have its parent set to the context loaded from "/app-config.xml"
.
@RunWith(SpringRunner.class)
@ContextHierarchy({
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
@ContextConfiguration(name = "child", locations = "/user-config.xml")
})
public class BaseTests {}
@ContextHierarchy(
@ContextConfiguration(
name = "child",
locations = "/test-user-config.xml",
inheritLocations = false
))
public class ExtendedTests extends BaseTests {}
Dirtying a context within a context hierarchy | |
---|---|
If |
When you use the DependencyInjectionTestExecutionListener
— which is configured by default — the dependencies of your test instances are injected from beans in the application context that you configured with @ContextConfiguration
. You may use setter injection, field injection, or both, depending on which annotations you choose and whether you place them on setter methods or fields. For consistency with the annotation support introduced in Spring 2.5 and 3.0, you can use Spring’s @Autowired
annotation or the @Inject
annotation from JSR 330.
Tip | |
---|---|
The TestContext framework does not instrument the manner in which a test instance is instantiated. Thus the use of |
Because @Autowired
is used to perform autowiring by type, if you have multiple bean definitions of the same type, you cannot rely on this approach for those particular beans. In that case, you can use @Autowired
in conjunction with @Qualifier
. As of Spring 3.0 you may also choose to use @Inject
in conjunction with @Named
. Alternatively, if your test class has access to its ApplicationContext
, you can perform an explicit lookup by using (for example) a call to applicationContext.getBean("titleRepository")
.
If you do not want dependency injection applied to your test instances, simply do not annotate fields or setter methods with @Autowired
or @Inject
. Alternatively, you can disable dependency injection altogether by explicitly configuring your class with @TestExecutionListeners
and omitting DependencyInjectionTestExecutionListener.class
from the list of listeners.
Consider the scenario of testing a HibernateTitleRepository
class, as outlined in the Goals section. The next two code listings demonstrate the use of @Autowired
on fields and setter methods. The application context configuration is presented after all sample code listings.
Note | |
---|---|
The dependency injection behavior in the following code listings is not specific to JUnit 4. The same DI techniques can be used in conjunction with any testing framework. The following examples make calls to static assertion methods such as |
The first code listing shows a JUnit 4 based implementation of the test class that uses @Autowired
for field injection.
@RunWith(SpringRunner.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
public class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
@Autowired
private HibernateTitleRepository titleRepository;
@Test
public void findById() {
Title title = titleRepository.findById(new Long(10));
assertNotNull(title);
}
}
Alternatively, you can configure the class to use @Autowired
for setter injection as seen below.
@RunWith(SpringRunner.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
public class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
private HibernateTitleRepository titleRepository;
@Autowired
public void setTitleRepository(HibernateTitleRepository titleRepository) {
this.titleRepository = titleRepository;
}
@Test
public void findById() {
Title title = titleRepository.findById(new Long(10));
assertNotNull(title);
}
}
The preceding code listings use the same XML context file referenced by the @ContextConfiguration
annotation (that is, repository-config.xml
), which looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- this bean will be injected into the HibernateTitleRepositoryTests class -->
<bean id="titleRepository" class="com.foo.repository.hibernate.HibernateTitleRepository">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- configuration elided for brevity -->
</bean>
</beans>
Note | |
---|---|
If you are extending from a Spring-provided test base class that happens to use The specified qualifier value indicates the specific |
Request and session scoped beans have been supported by Spring since the early years, and since Spring 3.2 it’s a breeze to test your request-scoped and session-scoped beans by following these steps.
- Ensure that a
WebApplicationContext
is loaded for your test by annotating your test class with@WebAppConfiguration
. - Inject the mock request or session into your test instance and prepare your test fixture as appropriate.
- Invoke your web component that you retrieved from the configured
WebApplicationContext
(i.e., via dependency injection). - Perform assertions against the mocks.
The following code snippet displays the XML configuration for a login use case. Note that the userService
bean has a dependency on a request-scoped loginAction
bean. Also, the LoginAction
is instantiated using SpEL expressions that retrieve the username and password from the current HTTP request. In our test, we will want to configure these request parameters via the mock managed by the TestContext framework.
Request-scoped bean configuration.
<beans>
<bean id="userService"
class="com.example.SimpleUserService"
c:loginAction-ref="loginAction" />
<bean id="loginAction" class="com.example.LoginAction"
c:username="#{request.getParameter('user')}"
c:password="#{request.getParameter('pswd')}"
scope="request">
<aop:scoped-proxy />
</bean>
</beans>
In RequestScopedBeanTests
we inject both the UserService
(i.e., the subject under test) and the MockHttpServletRequest
into our test instance. Within our requestScope()
test method we set up our test fixture by setting request parameters in the provided MockHttpServletRequest
. When the loginUser()
method is invoked on our userService
we are assured that the user service has access to the request-scoped loginAction
for the current MockHttpServletRequest
(i.e., the one we just set parameters in). We can then perform assertions against the results based on the known inputs for the username and password.
Request-scoped bean test.
@RunWith(SpringRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class RequestScopedBeanTests {
@Autowired UserService userService;
@Autowired MockHttpServletRequest request;
@Test
public void requestScope() {
request.setParameter("user", "enigma");
request.setParameter("pswd", "$pr!ng");
LoginResults results = userService.loginUser();
// assert results
}
}
The following code snippet is similar to the one we saw above for a request-scoped bean; however, this time the userService
bean has a dependency on a session-scoped userPreferences
bean. Note that the UserPreferences
bean is instantiated using a SpEL expression that retrieves the theme from the current HTTP session. In our test, we will need to configure a theme in the mock session managed by the TestContext framework.
Session-scoped bean configuration.
<beans>
<bean id="userService"
class="com.example.SimpleUserService"
c:userPreferences-ref="userPreferences" />
<bean id="userPreferences"
class="com.example.UserPreferences"
c:theme="#{session.getAttribute('theme')}"
scope="session">
<aop:scoped-proxy />
</bean>
</beans>
In SessionScopedBeanTests
we inject the UserService
and the MockHttpSession
into our test instance. Within our sessionScope()
test method we set up our test fixture by setting the expected "theme" attribute in the provided MockHttpSession
. When the processUserPreferences()
method is invoked on our userService
we are assured that the user service has access to the session-scoped userPreferences
for the current MockHttpSession
, and we can perform assertions against the results based on the configured theme.
Session-scoped bean test.
@RunWith(SpringRunner.class)
@ContextConfiguration
@WebAppConfiguration
public class SessionScopedBeanTests {
@Autowired UserService userService;
@Autowired MockHttpSession session;
@Test
public void sessionScope() throws Exception {
session.setAttribute("theme", "blue");
Results results = userService.processUserPreferences();
// assert results
}
}
In the TestContext framework, transactions are managed by the TransactionalTestExecutionListener
which is configured by default, even if you do not explicitly declare @TestExecutionListeners
on your test class. To enable support for transactions, however, you must configure a PlatformTransactionManager
bean in the ApplicationContext
that is loaded via @ContextConfiguration
semantics (further details are provided below). In addition, you must declare Spring’s @Transactional
annotation either at the class or method level for your tests.