DEV Community

Cover image for πŸš€ Level up Your Testing Game: Harness the Power of Testcontainers with Java and Spring Boot πŸ§ͺ
Sergio Marcial
Sergio Marcial

Posted on

πŸš€ Level up Your Testing Game: Harness the Power of Testcontainers with Java and Spring Boot πŸ§ͺ

Introduction

Testing is an integral part of software development. It ensures that our applications work as expected and guards against unexpected regressions. As engineers, it becomes crucial for us to explore reliable and efficient testing practices early in our careers. In this article, we will dive into integration and functional testing by implementing Testcontainers with Java and Spring Boot, unleashing the power of containerization in our testing workflow. Let's get started! πŸŽ‰

What are Integration and Functional Testing?

Integration testing involves validating the interaction between different components of an application, ensuring they work cohesively. On the other hand, functional testing verifies the functionality of individual units within the application. Both types of tests complement each other, providing comprehensive coverage to prevent potential bugs or issues in our software.

Introducing Testcontainers 🐳

Testcontainers is an open-source Java library that enables developers to seamlessly create, manage, and orchestrate Docker containers for isolated testing environments. By integrating Testcontainers with our testing framework, we can easily provision containers, such as databases or message brokers, while writing tests, ensuring accurate and efficient integration and functional testing.

Setting Up Testcontainers with Maven and Gradle πŸ“¦

To leverage the power of Testcontainers, we need to add the necessary dependencies in our build tool setups.

Maven Example 🧩

In your pom.xml file, include the following dependencies:

<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>testcontainers</artifactId>
  <version>1.19.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>junit-jupiter</artifactId>
  <version>1.19.0</version>
  <scope>test</scope>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Example πŸ§ͺ

In your build.gradle file, add the dependencies:

testImplementation 'org.testcontainers:testcontainers:1.19.0'
testImplementation 'org.testcontainers:junit-jupiter:1.19.0'
Enter fullscreen mode Exit fullscreen mode

In your build.gradle.kts file, add the dependencies:

testImplementation("org.testcontainers:testcontainers:1.19.0")
testImplementation("org.testcontainers:junit-jupiter:1.19.0")
Enter fullscreen mode Exit fullscreen mode

With these dependencies in place, we are ready to explore the magic of Testcontainers! ✨

Using Testcontainers with Spring Boot Example πŸ’»

Let's dive into a practical example of how to leverage Testcontainers with Spring Boot framework for integration testing:

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
@SpringBootTest
@TestPropertySource(locations="classpath:application-test.yaml")
public class MyIntegrationTests {

  @Container
  private static final PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:latest");

  // Your tests go here
}
Enter fullscreen mode Exit fullscreen mode

In this example, we have annotated our test class with @Testcontainers, defining it as a test container-aware class. We also added @SpringBootTest to enable Spring Boot-specific configuration and @TestPropertySource to load specific test properties.

Using the @Container annotation, we declare an instance of the PostgreSQL container. Testcontainers automatically starts and stops the container for each test execution. You can easily configure other containers such as MySQL or Redis, depending on your requirements.

You can also include the spring boot dependency

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-testcontainers</artifactId>
  <version>3.1.3</version>
  <scope>test</scope>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Gradle Example πŸ§ͺ

In your build.gradle file, add the dependencies:

testImplementation 'org.springframework.boot:spring-boot-testcontainers:3.1.3'
Enter fullscreen mode Exit fullscreen mode

In your build.gradle.kts file, add the dependencies:

testImplementation("org.springframework.boot:spring-boot-testcontainers:3.1.3")
Enter fullscreen mode Exit fullscreen mode

Using Testcontainers with Spring Boot Example containers as beans πŸ’»

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.containers.wait.strategy.Wait
import org.testcontainers.utility.DockerImageName
import java.time.Duration

@TestConfiguration(proxyBeanMethods = false)
class TestConfig {

    @Bean
    @ServiceConnection
    fun postgresSqlContainer(): PostgreSQLContainer<*> =
        PostgreSQLContainer(
            DockerImageName.parse("postgres:latest"),
        ).also {
            it.setWaitStrategy(
                Wait.defaultWaitStrategy()
                    .withStartupTimeout(Duration.ofSeconds(30)),
            )
        }.run {
            this.start()
            this
        }
}
Enter fullscreen mode Exit fullscreen mode

This will allows to re use the container across multiple tests without the need to restart.

For a complete example using Kotlin, check out this GitHub repository.

Advantages and Alternatives 😎

Testcontainers provide numerous benefits for integration and functional testing:

βœ… Isolation: Testcontainers create isolated environments for testing, avoiding interference with external dependencies or shared environments.

βœ… Reproducibility: Each test run starts with a clean environment, eliminating any issues caused by conflicting or stale state.

βœ… Ease of Use: Testcontainers seamlessly integrates with popular testing frameworks and reduces the complexity of setting up separate testing environments.

While Testcontainers is an excellent choice for testing, there are alternative tools available too. Some significant alternatives include Docker Compose, Mountebank, and WireMock. Each has its unique advantages, so you should choose based on your specific needs.

Conclusion πŸ”

By incorporating Testcontainers in your testing repertoire, you can achieve efficient and robust integration and functional testing with ease. Its seamless integration with Java and Spring Boot makes it a perfect choice for leveraging containerization in your test suites. Start your journey with Testcontainers today, and level up your testing game! πŸš€

Remember, investing time in learning reliable testing practices early in your career will pay dividends in the long run. Happy testing! πŸ§ͺ

Top comments (0)