Setting up Testcontainers in Gradle

Setting up Testcontainers in Gradle: Step-by-Step—Configuration Guide


While developing modern Java applications, it is quite common to require some external services such as databases, message brokers, or web servers in testing. Traditionally, this required a manual setup of those services, which was often error-prone and not easy to handle in different environments. Enter Testcontainers, a popular Java library that allows tests to run inside lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can be run in a Docker container.

This guide will lead you through the process of integration with Testcontainers using Gradle, from setting up the environment to running tests.

Prerequisites

 Ensure that you have the following installed in your system before proceeding: 
  • Java 8 or later: Testcontainers requires at least Java 8.
  • Gradle: The following tutorial is based on Gradle as the build tool.
  • Docker: As Testcontainers uses Docker to run the containers, you need to have Docker installed and running on your host machine.

Preparation of a Gradle project

You haven't set up a Gradle project yet? No problem, here's how to do it:

1. Create a new Gradle project:

 gradle init --type java-application

2. Project Structure:

   Your project structure, upon initialization, should look something like this:

   ├── build.gradle
   ├── settings.gradle
   └── src
       ├── main
       │   └── java
       └── test
└── java
   

Exclude Testcontainers from Your Build

Now, you will have to add dependencies for Testcontainers in your build.gradle file.

1. Add Testcontainer's dependencies

   Add the next dependencies in your build.gradle file.

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.3'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.3'
    testImplementation 'org.testcontainers:junit-jupiter:1.19.0'
    testImplementation 'org.testcontainers:mysql:1.19.0' // Example for MySQL, add other modules as needed
}

test {
    useJUnitPlatform()
}


This setup turns on JUnit 5 and Testcontainers. You can replace the MySQL module with any other module you might need, for example, `postgresql`, `redis`, etc.

2. Assure the availability of Docker:
   Testcontainers require the availability of Docker in your environment. Just assure that Docker is running by executing:

   docker --version

If Docker is installed correctly, this command will return the Docker version.

Writing a Simple Test with Testcontainers


Now that your project is configured to use Testcontainers, let's create a simple test.

1. Create a new test class in `src/test/java`, e.g. `MySQLContainerTest.java`:


import org.junit.jupiter.api.Test;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import static org.junit.jupiter.api.Assertions.assertTrue;

   @Testcontainers
   public class MySQLContainerTest {

       @Container
       public MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0.26")
               .withDatabaseName("testdb")
               .withUsername("test")
.withPassword("test");

       @Test
       void testMySQLContainerIsRunning() {
           mysql.start();
           assertTrue(mysql.isRunning());
       }
   }

In this example:
   - We create a `MySQLContainer` that is going to use the official MySQL 8.0.26 Docker image.
   - Then, the test will check whether a container with MySQL inside has started up and is ready.

2. Run the test:
   Now you can execute your test using Gradle:

   ./gradlew test

Gradle will execute the test, and Testcontainers will see the `@Container` annotation, transparently pull a Docker image for MySQL, start the container, execute the test, and afterward stop the container.

Advanced Configuration


Testcontainers comes with several advanced configurations that you may find useful. Some of these include the following ones: 

1. Reusing containers:
By default, Testcontainers starts and stops containers at the end of each test. You can speed up your tests a little by reusing containers, which you do by setting `testcontainers.reuse.enable` to `true` in `~/.testcontainers.properties`. This will enable container reuse.

testcontainers.reuse.enable=true

2. Customising container settings
The behavior of your containers can be further customized - for example, you may want to expose additional ports or mount volumes. Here's how you would do it:

MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0.26")
           .withExposedPorts(3306)
           .withEnv("MYSQL_ROOT_PASSWORD", "rootpass")
           .withFileSystemBind("/my/local/path", "/container/path");
   
3. Custom Docker network:
In case your tests require more than one container to interact with each other, you can put them on the same Docker network:

Network network = Network.newNetwork();

MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0.26")
        .withNetwork(network)
        .withNetworkAliases("mysql");

GenericContainer<?> app = new GenericContainer<>("myapp:latest")
 
.withNetwork(network)
           .withEnv("DB_HOST", "mysql");
   

Conclusion


Testcontainers with Gradle is the most powerful way to have external dependencies managed for your tests. The tests are run in Docker containers, so they are isolated and reproducible, identical across different environments. Testcontainers can be used with far greater flexibility and reliability to test databases, message queues, or even your own services.

Now, with this setup, you will be in a position to start writing more elaborate integration tests involving several services, knowing that each test will run in a clean and controlled environment.

Post a Comment

Previous Post Next Post