Dependency Injection of Test Fixtures
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
or related
annotations. 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.
If you are using JUnit Jupiter you may also optionally use constructor injection
(see Dependency Injection with SpringExtension
). For consistency with Spring’s annotation-based
injection support, you may also use Spring’s @Autowired
annotation or the @Inject
annotation from JSR-330 for field and setter injection.
For testing frameworks other than JUnit Jupiter, the TestContext framework does not
participate in instantiation of the test class. Thus, the use of @Autowired or
@Inject for constructors has no effect for test classes.
|
Although field injection is discouraged in production code, field injection is
actually quite natural in test code. The rationale for the difference is that you will
never instantiate your test class directly. Consequently, there is no need to be able to
invoke a public constructor or setter method on your test class.
|
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
. You can 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", TitleRepository.class)
.
If you do not want dependency injection applied to your test instances, 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.
The dependency injection behavior in the following code listings is not specific to JUnit Jupiter. The same DI techniques can be used in conjunction with any supported testing framework. The following examples make calls to static assertion methods, such as |
The first code listing shows a JUnit Jupiter based implementation of the test class that
uses @Autowired
for field injection:
-
Java
-
Kotlin
@ExtendWith(SpringExtension.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
@Autowired
HibernateTitleRepository titleRepository;
@Test
void findById() {
Title title = titleRepository.findById(new Long(10));
assertNotNull(title);
}
}
@ExtendWith(SpringExtension::class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
@Autowired
lateinit var titleRepository: HibernateTitleRepository
@Test
fun findById() {
val title = titleRepository.findById(10)
assertNotNull(title)
}
}
Alternatively, you can configure the class to use @Autowired
for setter injection, as
follows:
-
Java
-
Kotlin
@ExtendWith(SpringExtension.class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
HibernateTitleRepository titleRepository;
@Autowired
void setTitleRepository(HibernateTitleRepository titleRepository) {
this.titleRepository = titleRepository;
}
@Test
void findById() {
Title title = titleRepository.findById(new Long(10));
assertNotNull(title);
}
}
@ExtendWith(SpringExtension::class)
// specifies the Spring configuration to load for this test fixture
@ContextConfiguration("repository-config.xml")
class HibernateTitleRepositoryTests {
// this instance will be dependency injected by type
lateinit var titleRepository: HibernateTitleRepository
@Autowired
fun setTitleRepository(titleRepository: HibernateTitleRepository) {
this.titleRepository = titleRepository
}
@Test
fun findById() {
val title = titleRepository.findById(10)
assertNotNull(title)
}
}
The preceding code listings use the same XML context file referenced by the
@ContextConfiguration
annotation (that is, repository-config.xml
). The following
shows this configuration:
<?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
https://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>
If you are extending from a Spring-provided test base class that happens to use
The specified qualifier value indicates the specific |