Spring Boot HandBook

    Introduction :#

    Testing is a crucial part of software development, ensuring the quality and reliability of the system. It includes various test types, such as unit tests, integration tests, functional tests, and more, each focusing on different aspects of the system. Unit tests verify individual components, while integration tests ensure components work together as expected. Common testing annotations in Spring Boot, such as @SpringBootTest and @DataJpaTest, support efficient testing of different layers and components, contributing to a comprehensive testing strategy that ensures both functionality and performance.

    TestContainer#

    Use https://testcontainers.com/ to mock a real database, Use when mocking the repository or in Integration testing.

    1. Download the Docker Application from https://docs.docker.com/get-started/get-docker/ and run it.
      1. Alternatively, open Google and search for “docker download,” or visit one of the following links:
      2. https://www.docker.com/products/docker-desktop/
      3. Or https://docs.docker.com/desktop/install/windows-install/
      4. After downloading and installing Docker, open the application and run it.

    1. Add the following dependency:
      1. Go to https://start.spring.io/ configure your project settings, and then go to the 'Add Dependencies' section.
      2. Add the Testcontainers and PostgreSQL Driver dependencies.

    • After that, click on 'Explore.' Scroll down to confirm that these three dependencies have been added.

    • pom.xmlfile :
    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-testcontainers</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <scope>test</scope> </dependency>

    Why We Use Testcontainer#

    We use Testcontainers for several reasons, especially in integration testing:

    1. Realistic Testing Environment: Testcontainers allow us to run real databases (e.g., PostgreSQL, MySQL) or services (e.g., Redis, Kafka) in Docker containers. This provides a more realistic testing environment than in-memory databases like H2, ensuring that our tests reflect real-world scenarios.
    2. Isolated Test Environment: Each test runs in its own isolated container, ensuring that tests are consistent and do not interfere with each other. This isolation eliminates the problem of leftover data or conflicts between tests.
    3. Cross-Environment Consistency: Since Testcontainers uses Docker, the tests will run the same regardless of the underlying OS or setup. This ensures that your tests pass locally, in CI/CD pipelines, or on any developer's machine.
    4. Automated Setup and Teardown: Testcontainers automatically handle the lifecycle of Docker containers, starting them before the test and stopping them once the test is complete. This makes the process seamless and ensures a clean environment for each test.
    5. Avoiding In-Memory Database Limitations: In-memory databases like H2 may not fully replicate the behavior of a real relational database (e.g., differences in SQL syntax or transaction behavior). By using Testcontainers with a real database, we avoid these discrepancies.

    Running TestContainer#

    1. Create the following Test Configuration:

    @TestConfiguration(proxyBeanMethods = false) public class TestcontainersConfiguration {

    @Bean @ServiceConnection PostgreSQLContainer<?> postgresContainer() { return new PostgreSQLContainer<>(DockerImageName.parse("postgres:latest"));

    } }

    Code

    Create a Configuration class in our test file.

    import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.context.annotation.Bean; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.utility.DockerImageName; @TestConfiguration public class TestContainerConfiguration { @Bean @ServiceConnection PostgreSQLContainer<?> postgresContainer() { return new PostgreSQLContainer<>(DockerImageName.parse("postgres:latest")); } }
    • Import the Configuration in your test file:

    @DataJpaTest @Import(TestcontainersConfiguration.class) class EmployeeRepositoryTest {…}

    Code

    After creating the Configuration class, we need to import it into our test file. Currently, we are not using the H2 database for testing purposes. That's why we use @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE). The H2 database is typically used for lightweight testing, not production. In this case, we are only using Docker for database management.

    import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @Import(TestContainerConfiguration.class) @DataJpaTest @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) //to use docker container only class EmployeeRepositoryTest { @Autowired private EmployeeRepository employeeRepository; private EmployeeEntity employee; //Create a employee @BeforeEach void setup(){ employee = EmployeeEntity .builder() .id(1L) .name("Alic") .email("alice@gmail.com") .salary(100000.00).build(); } @Test //Test 'findByEmail()' when Email is valid then return Employee void testFindByEmail_whenEmailIsValid_thenReturnEmployee() { //Arrange, Given employeeRepository.save(employee); //Act, When List<EmployeeEntity> employeeEntityList = employeeRepository.findByEmail(employee.getEmail()); //Assert, Then assertThat(employeeEntityList).isNotNull(); assertThat(employeeEntityList).isNotEmpty(); assertThat(employeeEntityList.get(0).getEmail()).isEqualTo(employee.getEmail()); } @Test //Test 'findByEmail()' when Email is not found then return empty Employee List void testFindByEmail_whenEmailIsNotFound_thenReturnEmptyEmployeeList() { //Arrange, Given String email = "bob@gmail.com"; //Act, When List<EmployeeEntity> employeeEntityList =employeeRepository.findByEmail(employee.getEmail()); //Assert, Then assertThat(employeeEntityList).isNotNull(); assertThat(employeeEntityList).isEmpty(); } }
    • Output:

    Here, you can see that when your application is running, the status is ‘In use’.

    Once the test cases finish, the container stops. In the photo below, you can see the status is ‘Unused’.

    Here, we have already covered these aspects, which are highlighted in green in the image.

     

    Last updated on Dec 09, 2024