7/31/22, 5:50 PM Testing
Testing
Version 5.3.22
Back to index
1. Introduction to Spring Testing
2. Unit Testing
2.1. Mock Objects
2.1.1. Environment
2.1.2. JNDI
2.1.3. Servlet API
2.1.4. Spring Web Reactive
2.2. Unit Testing Support Classes
2.2.1. General Testing Utilities
2.2.2. Spring MVC Testing Utilities
3. Integration Testing
3.1. Overview
3.2. Goals of Integration Testing
3.2.1. Context Management and Caching
3.2.2. Dependency Injection of Test Fixtures
3.2.3. Transaction Management
3.2.4. Support Classes for Integration Testing
3.3. JDBC Testing Support
3.4. Annotations
3.4.1. Spring Testing Annotations
@BootstrapWith
@ContextConfiguration
@WebAppConfiguration
@ContextHierarchy
@ActiveProfiles
@TestPropertySource
@DynamicPropertySource
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 1/17
7/31/22, 5:50 PM Testing
@DirtiesContext
@TestExecutionListeners
@RecordApplicationEvents
@Commit
@Rollback
@BeforeTransaction
@AfterTransaction
@Sql
@SqlConfig
@SqlMergeMode
@SqlGroup
3.4.2. Standard Annotation Support
3.4.3. Spring JUnit 4 Testing Annotations
@IfProfileValue
@ProfileValueSourceConfiguration
@Timed
@Repeat
3.4.4. Spring JUnit Jupiter Testing Annotations
@SpringJUnitConfig
@SpringJUnitWebConfig
@TestConstructor
@NestedTestConfiguration
@EnabledIf
@DisabledIf
3.4.5. Meta-Annotation Support for Testing
3.5. Spring TestContext Framework
3.5.1. Key Abstractions
TestContext
TestContextManager
TestExecutionListener
Context Loaders
3.5.2. Bootstrapping the TestContext Framework
3.5.3. TestExecutionListener Configuration
Registering TestExecutionListener Implementations
Automatic Discovery of Default TestExecutionListener Implementations
Ordering TestExecutionListener Implementations
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 2/17
7/31/22, 5:50 PM Testing
Merging TestExecutionListener Implementations
3.5.4. Application Events
3.5.5. Test Execution Events
Exception Handling
Asynchronous Listeners
3.5.6. Context Management
Context Configuration with XML resources
Context Configuration with Groovy Scripts
Context Configuration with Component Classes
Mixing XML, Groovy Scripts, and Component Classes
Context Configuration with Context Initializers
Context Configuration Inheritance
Context Configuration with Environment Profiles
Context Configuration with Test Property Sources
Context Configuration with Dynamic Property Sources
Loading a WebApplicationContext
Context Caching
Context Hierarchies
3.5.7. Dependency Injection of Test Fixtures
3.5.8. Testing Request- and Session-scoped Beans
3.5.9. Transaction Management
Test-managed Transactions
Enabling and Disabling Transactions
Transaction Rollback and Commit Behavior
Programmatic Transaction Management
Running Code Outside of a Transaction
Configuring a Transaction Manager
Demonstration of All Transaction-related Annotations
3.5.10. Executing SQL Scripts
Executing SQL scripts programmatically
Executing SQL scripts declaratively with @Sql
3.5.11. Parallel Test Execution
3.5.12. TestContext Framework Support Classes
Spring JUnit 4 Runner
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 3/17
7/31/22, 5:50 PM Testing
Spring JUnit 4 Rules
JUnit 4 Support Classes
SpringExtension for JUnit Jupiter
Dependency Injection with SpringExtension
@Nested test class configuration
TestNG Support Classes
3.6. WebTestClient
3.6.1. Setup
Bind to Controller
Bind to ApplicationContext
Bind to Router Function
Bind to Server
Client Config
3.6.2. Writing Tests
No Content
JSON Content
Streaming Responses
MockMvc Assertions
3.7. MockMvc
3.7.1. Overview
Static Imports
Setup Choices
Setup Features
Performing Requests
Defining Expectations
Async Requests
Streaming Responses
Filter Registrations
MockMvc vs End-to-End Tests
Further Examples
3.7.2. HtmlUnit Integration
Why HtmlUnit Integration?
MockMvc and HtmlUnit
MockMvc and WebDriver
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 4/17
7/31/22, 5:50 PM Testing
MockMvc and Geb
3.8. Testing Client Applications
3.8.1. Static Imports
3.8.2. Further Examples of Client-side REST Tests
4. Further Resources
This chapter covers Spring’s support for integration testing and best practices for unit testing.
The Spring team advocates test-driven development (TDD). The Spring team has found that the
correct use of inversion of control (IoC) certainly does make both unit and integration testing
easier (in that the presence of setter methods and appropriate constructors on classes makes
them easier to wire together in a test without having to set up service locator registries and
similar structures).
1. Introduction to Spring Testing
Testing is an integral part of enterprise software development. This chapter focuses on the value
added by the IoC principle to unit testing and on the benefits of the Spring Framework’s support
for integration testing. (A thorough treatment of testing in the enterprise is beyond the scope of
this reference manual.)
2. Unit Testing
Dependency injection should make your code less dependent on the container than it would be
with traditional Java EE development. The POJOs that make up your application should be
testable in JUnit or TestNG tests, with objects instantiated by using the new operator, without
Spring or any other container. You can use mock objects (in conjunction with other valuable
testing techniques) to test your code in isolation. If you follow the architecture recommendations
for Spring, the resulting clean layering and componentization of your codebase facilitate easier
unit testing. For example, you can test service layer objects by stubbing or mocking DAO or
repository interfaces, without needing to access persistent data while running unit tests.
True unit tests typically run extremely quickly, as there is no runtime infrastructure to set up.
Emphasizing true unit tests as part of your development methodology can boost your
productivity. You may not need this section of the testing chapter to help you write effective unit
tests for your IoC-based applications. For certain unit testing scenarios, however, the Spring
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 5/17
7/31/22, 5:50 PM Testing
Framework provides mock objects and testing support classes, which are described in this
chapter.
2.1. Mock Objects
Spring includes a number of packages dedicated to mocking:
Environment
JNDI
Servlet API
Spring Web Reactive
2.1.1. Environment
The org.springframework.mock.env package contains mock implementations of
the Environment and PropertySource abstractions (see Bean Definition
Profiles and PropertySource Abstraction). MockEnvironment and MockPropertySource are useful
for developing out-of-container tests for code that depends on environment-specific properties.
2.1.2. JNDI
The org.springframework.mock.jndi package contains a partial implementation of the JNDI SPI,
which you can use to set up a simple JNDI environment for test suites or stand-alone
applications. If, for example, JDBC DataSource instances get bound to the same JNDI names in
test code as they do in a Java EE container, you can reuse both application code and
configuration in testing scenarios without modification.
The mock JNDI support in the org.springframework.mock.jndi package is officially
deprecated as of Spring Framework 5.2 in favor of complete solutions from third parties
such as Simple-JNDI.
2.1.3. Servlet API
The org.springframework.mock.web package contains a comprehensive set of Servlet API mock
objects that are useful for testing web contexts, controllers, and filters. These mock objects are
targeted at usage with Spring’s Web MVC framework and are generally more convenient to use
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 6/17
7/31/22, 5:50 PM Testing
than dynamic mock objects (such as EasyMock) or alternative Servlet API mock objects (such
as MockObjects).
Since Spring Framework 5.0, the mock objects in org.springframework.mock.web are based
on the Servlet 4.0 API.
The Spring MVC Test framework builds on the mock Servlet API objects to provide an integration
testing framework for Spring MVC. See MockMvc.
2.1.4. Spring Web Reactive
The org.springframework.mock.http.server.reactive package contains mock implementations
of ServerHttpRequest and ServerHttpResponse for use in WebFlux applications.
The org.springframework.mock.web.server package contains a mock ServerWebExchange that
depends on those mock request and response objects.
Both MockServerHttpRequest and MockServerHttpResponse extend from the same abstract base
classes as server-specific implementations and share behavior with them. For example, a mock
request is immutable once created, but you can use the mutate() method
from ServerHttpRequest to create a modified instance.
In order for the mock response to properly implement the write contract and return a write
completion handle (that is, Mono<Void> ), it by default uses a Flux with cache().then() , which
buffers the data and makes it available for assertions in tests. Applications can set a custom write
function (for example, to test an infinite stream).
The WebTestClient builds on the mock request and response to provide support for testing
WebFlux applications without an HTTP server. The client can also be used for end-to-end tests
with a running server.
2.2. Unit Testing Support Classes
Spring includes a number of classes that can help with unit testing. They fall into two categories:
General Testing Utilities
Spring MVC Testing Utilities
2.2.1. General Testing Utilities
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 7/17
7/31/22, 5:50 PM Testing
The org.springframework.test.util package contains several general purpose utilities for use
in unit and integration testing.
ReflectionTestUtils is a collection of reflection-based utility methods. You can use these
methods in testing scenarios where you need to change the value of a constant, set a non-
public field, invoke a non- public setter method, or invoke a non- public configuration or
lifecycle callback method when testing application code for use cases such as the following:
ORM frameworks (such as JPA and Hibernate) that condone private or protected field
access as opposed to public setter methods for properties in a domain entity.
Spring’s support for annotations (such as @Autowired , @Inject , and @Resource ), that
provide dependency injection for private or protected fields, setter methods, and
configuration methods.
Use of annotations such as @PostConstruct and @PreDestroy for lifecycle callback methods.
AopTestUtils is a collection of AOP-related utility methods. You can use these methods to
obtain a reference to the underlying target object hidden behind one or more Spring proxies. For
example, if you have configured a bean as a dynamic mock by using a library such as EasyMock
or Mockito, and the mock is wrapped in a Spring proxy, you may need direct access to the
underlying mock to configure expectations on it and perform verifications. For Spring’s core AOP
utilities, see AopUtils and AopProxyUtils .
2.2.2. Spring MVC Testing Utilities
The org.springframework.test.web package contains ModelAndViewAssert , which you can use
in combination with JUnit, TestNG, or any other testing framework for unit tests that deal with
Spring MVC ModelAndView objects.
Unit testing Spring MVC Controllers
To unit test your Spring MVC Controller classes as POJOs,
use ModelAndViewAssert combined with MockHttpServletRequest , MockHttpSession , and
so on from Spring’s Servlet API mocks. For thorough integration testing of your Spring MVC
and REST Controller classes in conjunction with
your WebApplicationContext configuration for Spring MVC, use the Spring MVC Test
Framework instead.
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 8/17
7/31/22, 5:50 PM Testing
3. Integration Testing
This section (most of the rest of this chapter) covers integration testing for Spring applications. It
includes the following topics:
Overview
Goals of Integration Testing
JDBC Testing Support
Annotations
Spring TestContext Framework
MockMvc
3.1. Overview
It is important to be able to perform some integration testing without requiring deployment to your
application server or connecting to other enterprise infrastructure. Doing so lets you test things
such as:
The correct wiring of your Spring IoC container contexts.
Data access using JDBC or an ORM tool. This can include such things as the correctness of
SQL statements, Hibernate queries, JPA entity mappings, and so forth.
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.
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, which allows instrumentation of tests in various environments, including JUnit, TestNG,
and others.
3.2. Goals of Integration Testing
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 9/17
7/31/22, 5:50 PM Testing
Spring’s integration testing support has the following primary goals:
To manage Spring IoC container caching between tests.
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.
3.2.1. Context Management and Caching
The Spring TestContext Framework provides consistent loading of
Spring ApplicationContext instances and WebApplicationContext instances 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 component 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 Context Management and Context Caching with the TestContext framework.
3.2.2. Dependency Injection of Test Fixtures
When the TestContext framework loads your application context, it can optionally configure
instances of your test classes by using Dependency Injection. This provides a convenient
mechanism for setting up test fixtures by using preconfigured beans from your application
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 10/17
7/31/22, 5:50 PM Testing
context. A strong benefit here is that you can reuse application contexts across various testing
scenarios (for example, for configuring Spring-managed object graphs, transactional
proxies, DataSource instances, and others), thus avoiding the need to duplicate complex test
fixture setup for individual test cases.
As an example, consider a 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.
3.2.3. Transaction Management
One common issue in tests that access a real database is their effect on the state of the
persistence store. Even when you use 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 of a transaction.
The TestContext framework addresses this issue. By default, the framework creates and rolls
back a transaction for each test. You can write code that can assume the existence of a
transaction. If you call transactionally proxied objects in your tests, they 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 rolls back by default, and the database returns to its state prior to execution of the
test. Transactional support is provided to a test by using 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), you can tell the TestContext framework to cause the
transaction to commit instead of roll back by using the @Commit annotation.
See transaction management with the TestContext framework.
3.2.4. Support Classes for Integration Testing
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 11/17
7/31/22, 5:50 PM Testing
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 let you 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. You can use such
queries to confirm database state both before 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.
3.3. JDBC Testing Support
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 table.
countRowsInTableWhere(..) : Counts the number of rows in the given table by using the
provided WHERE clause.
deleteFromTables(..) : Deletes all rows from the specified tables.
deleteFromTableWhere(..) : Deletes rows from the given table by using the
provided WHERE clause.
dropTables(..) : Drops the specified tables.
AbstractTransactionalJUnit4SpringContextTests and AbstractTransactionalTestNGSpringC
ontextTests provide convenience methods that delegate to the aforementioned methods
in JdbcTestUtils .
The spring-jdbc module provides support for configuring and launching an embedded
database, which you can use in integration tests that interact with a database. For details,
see Embedded Database Support and Testing Data Access Logic with an Embedded
Database.
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 12/17
7/31/22, 5:50 PM Testing
3.4. Annotations
This section covers annotations that you can use when you test Spring applications. It includes
the following topics:
Spring Testing Annotations
Standard Annotation Support
Spring JUnit 4 Testing Annotations
Spring JUnit Jupiter Testing Annotations
Meta-Annotation Support for Testing
3.4.1. Spring Testing Annotations
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. See the
corresponding javadoc for further information, including default attribute values, attribute aliases,
and other details.
Spring’s testing annotations include the following:
@BootstrapWith
@ContextConfiguration
@WebAppConfiguration
@ContextHierarchy
@ActiveProfiles
@TestPropertySource
@DynamicPropertySource
@DirtiesContext
@TestExecutionListeners
@RecordApplicationEvents
@Commit
@Rollback
@BeforeTransaction
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 13/17
7/31/22, 5:50 PM Testing
@AfterTransaction
@Sql
@SqlConfig
@SqlMergeMode
@SqlGroup
@BootstrapWith
@BootstrapWith is a class-level annotation that you can use to configure how the Spring
TestContext Framework is bootstrapped. Specifically, you can use @BootstrapWith to specify a
custom TestContextBootstrapper . See the section on bootstrapping the TestContext
framework for further details.
@ContextConfiguration
@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
component classes used to load the context.
Resource locations are typically XML configuration files or Groovy scripts located in the
classpath, while component classes are typically @Configuration classes. However, resource
locations can also refer to files and scripts in the file system, and component classes can
be @Component classes, @Service classes, and so on. See Component Classes for further
details.
The following example shows a @ContextConfiguration annotation that refers to an XML file:
Java Kotlin
@ContextConfiguration("/test-config.xml")
class XmlApplicationContextTests {
// class body...
Referring to an XML file.
The following example shows a @ContextConfiguration annotation that refers to a class:
Java Kotlin
@ContextConfiguration(classes = TestConfig.class)
class ConfigClassApplicationContextTests {
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 14/17
7/31/22, 5:50 PM Testing
// class body...
Referring to a class.
As an alternative or in addition to declaring resource locations or component classes, you can
use @ContextConfiguration to declare ApplicationContextInitializer classes. The following
example shows such a case:
Java Kotlin
@ContextConfiguration(initializers = CustomContextInitializer.class)
class ContextInitializerTests {
// class body...
Declaring an initializer class.
You can optionally use @ContextConfiguration 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 initializers and either resource locations or component classes .
The following example uses both a location and a loader:
Java Kotlin
@ContextConfiguration(locations = "/test-context.xml", loader = CustomContextLoader.class)
class CustomLoaderXmlApplicationContextTests {
// class body...
Configuring both a location and a custom loader.
@ContextConfiguration provides support for inheriting resource locations or configuration
classes as well as context initializers that are declared by superclasses or enclosing
classes.
See Context Management, @Nested test class configuration, and
the @ContextConfiguration javadocs for further details.
@WebAppConfiguration
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 15/17
7/31/22, 5:50 PM Testing
@WebAppConfiguration is a class-level annotation that you can use 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 is loaded for the test, using the default value
of "file:src/main/webapp" for the path to the root of the web application (that is, 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 .
The following example shows how to use the @WebAppConfiguration annotation:
Java Kotlin
@ContextConfiguration
@WebAppConfiguration
class WebAppTests {
// class body...
To override the default, you can specify a different base resource path by using 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. The following
example shows how to specify a classpath resource:
Java Kotlin
@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources")
class WebAppTests {
// class body...
Specifying a classpath resource.
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 javadoc
for further details.
@ContextHierarchy
@ContextHierarchy is a class-level annotation that is used to define a hierarchy
of ApplicationContext instances 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 ( @ContextHierarchy can also be used within a test class hierarchy):
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 16/17
7/31/22, 5:50 PM Testing
Java Kotlin
@ContextHierarchy({
@ContextConfiguration("/parent-config.xml"),
@ContextConfiguration("/child-config.xml")
})
class ContextHierarchyTests {
// class body...
Java Kotlin
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = AppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing 17/17