Configuring the Application Context of Our Integration Tests
After we have finished this lesson, we:
- Can identify the key features of the Spring Test Framework’s integration testing support.
- Know how we can configure the application context of our integration tests when we are writing integration tests for a normal Spring application.
- Can configure the application context of our integration tests when we are writing integration tests for a Spring Boot application.
This lesson assumes that:
- You can use custom JUnit 4 test runners
- You know how you can get the Spring Test dependency with Maven or Gradle
- You know how you can run integration tests with Maven or Gradle
The Key Features of the Integration Testing Support of the Spring Test Framework
The integration testing support of the Spring Test Framework has three keys features that are described in the following:
First, it supports dependency injection. This means that we can configure the system under test by using an application context configuration class or an XML configuration file AND inject the required Spring beans into our integration test classes. In other words, we can reuse our application context configuration and avoid writing complex setup code in our test classes.
Second, it caches the loaded application contexts. If we create new components and initialize the created objects before every integration test, we might create a test suite that is a lot slower than it could be because initializing the required components can take a lot of time.
Spring Test solves this problem by caching the loaded application context when it is loaded for the first time. If other integration tests need to use same application context, Spring Test returns the cached application context. This helps us to create test suites that are as fast as possible and do not reduce our productivity.
Third, it provides support for transaction management. When we write tests that use a persistent storage such as a relational database, we have to solve two problems:
- We have to ensure that the changes made to our database do not affect future tests.
- We have to figure out a way to run our tests inside a transaction because otherwise we cannot write tests for code that makes changes to our database.
By default, the Spring Test framework creates and rolls back a transaction for each test. Also, if our tests invoke @Transactional methods, these transactionally proxied objects will behave correctly.
Additional Reading:
We are now ready to configure the application context of our first integration test. However, before we can do it, we have to take a look at the system under test.
Introduction to System Under Test
The system under test consists of two classes that are described in the following:
First, the MessageRepository class has a single method called findMessage() that simply returns the string: Hello World!.
The source code of the MessageRepository class looks as follows:
1import org.springframework.stereotype.Repository;
2
3@Repository
4class MessageRepository {
5
6 String findMessage() {
7 return "Hello World!";
8 }
9}
Second, the MessageService class has a single method (getMessage()) that obtains the message by invoking the findMessage() method of the MessageRepository class and returns the obtained message.
The source code of the MessageService class looks as follows:
1import org.springframework.beans.factory.annotation.Autowired;
2import org.springframework.stereotype.Service;
3
4@Service
5public class MessageService {
6
7 private final MessageRepository repository;
8
9 @Autowired
10 MessageService(MessageRepository repository) {
11 this.repository = repository;
12 }
13
14 public String getMessage() {
15 return repository.findMessage();
16 }
17}
We are now ready to configure the application context of our first integration test. Let’s find out how we can do it.
Configuring the Application Context of Our Integration Tests
Next we will learn to configure the application context of our integration tests when we are writing integration tests for a “normal” Spring application and for an application that uses Spring Boot.
Using Spring Framework
If we are writing integration tests for an application that uses Spring Framework, we can configure the application context of our integration tests by following these steps:
First, we have to ensure that our integration tests are run by the SpringJunit4ClassRunner class. This runner ensures that we can use the features of the Spring Test framework when we are writing integration tests with JUnit 4.
After we have configured the JUnit 4 runner that runs our integration tests, the source code of our test class looks as follows:
1import org.junit.experimental.categories.Category;
2import org.junit.runner.RunWith;
3import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
4
5@RunWith(SpringJUnit4ClassRunner.class)
6@Category(IntegrationTest.class)
7public class GetMessageTest {
8
9}
Second, we have to configure the application context configuration classes or XML configuration files which configure the application context of our integration tests. We can do this by annotating our test class with the @ContextConfiguration annotation.
After we have configured the required application context configuration classes, the source code of our test class looks as follows:
1import org.junit.experimental.categories.Category;
2import org.junit.runner.RunWith;
3import org.springframework.test.context.ContextConfiguration;
4import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
5
6import static org.assertj.core.api.Assertions.assertThat;
7
8@RunWith(SpringJUnit4ClassRunner.class)
9@ContextConfiguration(classes = {MessageContext.class})
10@Category(IntegrationTest.class)
11public class GetMessageTest {
12
13}
The MessageContext class is a simple application context configuration class. Its source code looks as follows:
1import org.springframework.context.annotation.ComponentScan;
2import org.springframework.context.annotation.Configuration;
3
4@Configuration
5@ComponentScan
6public class MessageContext {
7}
If you are using XML configuration files, you can configure the required application context configuration files by setting the value of the locations attribute of the @ContextConfiguration annotation.
For example, if you want to load your application context by using an application context configuration file called: message-context.xml that is found from the classpath, the source code of your test class looks as follows:
1@RunWith(SpringJUnit4ClassRunner.class)
2@ContextConfiguration(locations = {"classpath:message-context.xml"})
3@Category(IntegrationTest.class)
4public class GetMessageTest {
5
6}
Additional Reading:
We are done. We can now write tests for Spring beans that are managed by the Spring container. For example, if we want to ensure that the getMessage() method of the MessageService class is working as expected, we have to follow these steps:
- Inject the
MessageServicebean into our test class. - Implement a test method which verifies that the
getMessage()method returns the string: ‘Hello World!’.
After we have implemented our integration test, the source code of our test class looks as follows:
1import org.junit.Test;
2import org.junit.experimental.categories.Category;
3import org.junit.runner.RunWith;
4import org.springframework.beans.factory.annotation.Autowired;
5import org.springframework.test.context.ContextConfiguration;
6import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
7
8import static org.assertj.core.api.Assertions.assertThat;
9
10@RunWith(SpringJUnit4ClassRunner.class)
11@ContextConfiguration(classes = {MessageContext.class})
12@Category(IntegrationTest.class)
13public class GetMessageTest {
14
15 @Autowired
16 private MessageService service;
17
18 @Test
19 public void shouldReturnHelloWorldMessage() {
20 String message = service.getMessage();
21 assertThat(message).isEqualTo("Hello World!");
22 }
23}
Let’s move on and find out how we can configure the application context of our integration tests if we are using Spring Boot.
Using Spring Boot
If we are writing integration tests for a Spring Boot application, we can configure the application context of our integration tests by using the technique that was described in the previous section.
That being said, I think that we should configure the application context of our integration tests by using the @SpringBootTest annotation because it allows us to use Spring Boot specific features when we write integration tests for our application.
After we have replace the @ContextConfiguration annotation with the @SpringBootTest annotation, the source code of our test class looks follows:
1import org.junit.Test;
2import org.junit.experimental.categories.Category;
3import org.junit.runner.RunWith;
4import org.springframework.beans.factory.annotation.Autowired;
5import org.springframework.boot.test.context.SpringBootTest;
6import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
7
8import static org.assertj.core.api.Assertions.assertThat;
9
10@RunWith(SpringJUnit4ClassRunner.class)
11@SpringBootTest(classes = {MessageApplication.class})
12@Category(IntegrationTest.class)
13public class GetMessageTest {
14
15 @Autowired
16 private MessageService service;
17
18 @Test
19 public void shouldReturnHelloWorldMessage() {
20 String message = service.getMessage();
21 assertThat(message).isEqualTo("Hello World!");
22 }
23}
The MessageApplication class is a simple Spring Boot application class. Its source code looks as follows:
1import org.springframework.boot.SpringApplication;
2import org.springframework.boot.autoconfigure.SpringBootApplication;
3
4@SpringBootApplication
5public class MessageApplication {
6
7 public static void main(String[] args) {
8 SpringApplication.run(MessageApplication.class, args);
9 }
10}
Additional Reading:
Let’s summarize what we learned from this lesson.
Summary
This lesson has taught us three things:
- We have ensure that our integration tests are run by the
SpringJUnit4ClassRunnerclass. - If we are writing integration tests for an application that uses Spring Framework, we can configure the used application context configuration classes or XML files by using the
@ContextConfigurationannotation. - If we are writing integration tests for an application that uses Spring Boot, we can configure the used application context configuration class by using the
@SpringBootTestannotation.