Spring
Testing
Database
JUnit
Test Automation

How to re-create database before each test in Spring?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

When it comes to software development, especially in testing environments, it is essential to ensure that each test runs in a freshly prepared environment. This avoids inter-test dependencies and ensures the reliability and predictability of test results. In the context of database testing with Spring, this often means re-creating or resetting the database before each test. Here's a comprehensive guide on how to achieve this in Spring applications.

Why Re-Create a Database Before Each Test?

  1. Isolation: Tests are isolated from each other, which helps in identifying specific issues rather than introducing test flakiness due to shared state.
  2. Repeatability: Allows for consistent test results every time they are run.
  3. Independence: Each test can function independently of any side effects caused by other tests.
  4. Clean Slate: Ensures that changes made in one test do not affect subsequent tests.

Approaches to Re-Create a Database in Spring

Several strategies can be employed to ensure that you have a clean database state before each test in a Spring application:

Using In-Memory Databases

For applications with relatively simple database setups or for unit testing purposes, using an in-memory database such as H2 or HSQLDB might be an ideal choice:

  • Configure your Spring application to use an in-memory database profile during testing.
  • Spring Boot makes it easy by auto-configuring an embedded database if available on the classpath.

Example Setup:

yaml
1# application-test.yaml
2spring:
3  datasource:
4    url: jdbc:h2:mem:testdb
5    driver-class-name: org.h2.Driver
6    username: sa
7    password:
8  h2:
9    console:
10      enabled: true
java
1@RunWith(SpringRunner.class)
2@SpringBootTest
3@ActiveProfiles("test")
4public class MyServiceTest {
5
6    @Autowired
7    private MyService myService;
8
9    @Test
10    public void testServiceFunctionality() {
11        // Your test code here
12    }
13}

Using Flyway or Liquibase for Schema Initialization

For more complex scenarios, especially when the database schema and initial data setup need to be controlled, tools like Flyway or Liquibase are extremely useful:

  • Use migration scripts to set up the database schema.
  • Ensure these scripts run before each test.

Flyway Example:

  1. Create SQL migration scripts under src/main/resources/db/migration.
  2. Flyway will automatically execute these scripts on application startup.
java
1@RunWith(SpringRunner.class)
2@SpringBootTest
3@TestExecutionListeners({DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class})
4@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
5public class MyRepositoryTest {
6
7    @PersistenceContext
8    private EntityManager entityManager;
9
10    @Test
11    public void testRepositoryFunctionality() {
12        // Test logic here
13    }
14}

Leveraging Spring's @DirtiesContext

Sometimes it's necessary to not only re-create the database but also refresh the entire application context to clear state:

  • Use @DirtiesContext to indicate that the application context should be cleaned up after the test class or method.

Example Usage:

java
1@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
2public class MyServiceIntegrationTest {
3
4    @Autowired
5    private MyService myService;
6
7    @Test
8    public void testFirstScenario() {
9        // Test implementation
10    }
11
12    @Test
13    public void testSecondScenario() {
14        // Test implementation
15    }
16}

Summary Table

Here’s a quick look at the key strategies and their considerations:

ApproachProsCons
In-Memory DatabaseFast setup, great for unit testsMay not fully replicate production environments
Flyway/LiquibaseWorks well for complex, production-like setupsRequires managing additional migration scripts
@DirtiesContextEnsures complete application context resetSlower, as it redeploys the entire Spring context

Best Practices

  1. Choose Your Approach Wisely: Depending on your database and testing requirements, choose between in-memory databases or database versioning tools.
  2. Maintain Test Hygiene: Use annotations judiciously to maintain efficient test execution.
  3. Keep Migration Scripts Up-to-Date: Regularly update and maintain your migration scripts to align with the latest database schema and data requirements.

Conclusion

Re-creating your database before each test in a Spring application can be crucial for ensuring test reliability, repeatability, and independence. By leveraging Spring’s capabilities along with tools like Flyway, Liquibase, and embedded databases, you create a robust testing environment that accurately reflects your application's data state. This not only improves test quality but also speeds up the development cycle by catching issues early in the testing phase.


Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.