The Integration Test with Spring Boot

Mert Bahardoğan
5 min readJan 13, 2024

--

We will dive deep into the topic of the Integration Test, which is the last article in the series, both theoretically and practically. I recommend that you read the previous articles in the series, The Test Series-Introduction and The Unit Test, before starting to read this one.

Introduction

My previous articles mentioned that the Unit Test topic is the most crucial. In this article, we will focus on the section where we will test whether components work together seamlessly. After explaining the theoretical concepts, we will code a sustainable Integration test infrastructure and tests using JUnit and Spring Boot libraries. You can access the GitHub repository where this entire article series is located from the link below.

The Integration Test

Integration tests are a type of testing that verifies whether different software components work together seamlessly when combined in an environment. These tests help us understand how the application behaves in real-world scenarios and verify whether the components are in harmony.

Integration tests differ significantly from unit tests in that they focus on testing the interactions of components by ensuring the actual functioning of the Spring Application Context. This makes them slower than unit tests, as beans are created and injected. Writing an integration test for each exit point may be sufficient and more efficient, as it provides a holistic verification of the system’s behavior.

As seen in the test pyramid, there shouldn’t be an excessive number of integration tests. Unit tests already cover potential case scenarios. Unnecessary or improper use of these tests can lead to performance issues.

Let’s Code

Integration tests verify whether the integration has been successfully integrated from the outermost layer to the innermost layer. Therefore, they should be conducted with data that is close to the live environment. Hence, the context should stand up, if there are properties used in the project, they should be given separately for integration testing. A temporary database should be set up with the same constraints and test data. Since the tests should not affect each other, if a single context is to stand up, the changes in the database should be cleaned after each test or a separate context should be stood up for each test. Now let’s examine our code.

I wrote the AbstractIntegrationTest class to avoid code repetition by using an abstract class and to transfer common behaviors to all integration tests, I recommend you read this class in detail.

Let’s learn the annotations we use in the abstract class, and also you can use these annotations directly in your test class without the abstract class.

  1. @TestInstance(Lifecycle.PER_CLASS): This annotation specifies how test instances should be created. The value Lifecycle.PER_CLASS ensures that one test instance is created per test class.
  2. @AutoConfigureMockMvc: This annotation enables the automatic configuration of MockMvc in Spring MVC tests. It is used to test MVC applications.
  3. @EnableConfigurationProperties: This annotation enables the configuration properties of the application. Configuration properties typically contain values defined in application.properties or application.yml files.
  4. @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD): This annotation cleans the Spring context after each test method. This ensures independence between tests.
  5. @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT): This annotation starts the Spring Boot application in a test environment. The webEnvironment parameter specifies the web environment to be tested, and the value RANDOM_PORT ensures the selection of a random port number.
  6. @Autowired is used in Spring to automatically inject dependencies, making it easier to manage and organize the components of an application.

Lastly, as I mentioned above, we define application.properties under the test package so that our tests do not affect each other.

Also, I want to mention two more topics of integration testing.

DataJPATest

@DataJpaTest annotation is a testing annotation used in applications developed with Spring Framework and Spring Data JPA. Specifically designed for integration tests, this annotation is commonly employed to test database operations. Below is a brief explanation of its purpose:

  1. Database Context: @DataJpaTest loads the database context of a Spring Data JPA application, enabling the test class to interact with the relevant database.
  2. Loading Spring Beans: This annotation selectively loads beans related to Spring Data JPA. This prevents unnecessary additional beans from being loaded, contributing to faster test execution.
  3. Database Test Container: Typically, @DataJpaTest is used in conjunction with a lightweight database test container instead of a real database. This ensures tests run in an isolated environment without using a real database for testing purposes.
  4. Transaction Management: @DataJpaTest automatically starts a transaction for each test method and rolls back the transaction at the end of the test. This prevents tests from affecting each other, maintaining consistent test results.

This annotation is often used in Spring Boot projects, leveraging the testing infrastructure provided by Spring to facilitate the testing of database operations.

Contract Testing

Contract testing plays a crucial role, particularly in scenarios involving microservices architectures or the integration of different independent components to form a system. It is utilized to verify the communication between two or more software components and to test the contracts between these components.

This testing type focuses on validating the agreements or contracts between a service provider and a service consumer. While the service provider commits to offering a specific service, the service consumer expects to utilize this service. Contract tests are employed to confirm the validity of these contracts between components.

Finally, when we run our tests under the integration package, we can see that it takes 777ms even though we created separate contexts.

Conclusion

We have covered the most important test types in software engineering. It is crucial to acknowledge the significance of testing, as it saves us from additional costs and protects our project.

Thanks for reading the article to the end. If you have any questions related to these topics, please do not hesitate to contact me via LinkedIn. See you in the next articles.

Resources

--

--

Mert Bahardoğan

Software Engineer at Trendyol | Co-Organizer of Türkiye Java Community