Getting Started with Testcontainers for Java

Introduction

In modern software development, it is crucial to write robust and reliable tests to ensure the quality of your applications. One essential aspect of testing is dealing with dependencies, such as databases or external services. Testcontainers is an excellent Java library that provides lightweight, disposable containers for running dependencies during tests. In this tutorial, we will explore how to use Testcontainers to set up a GenericContainer and a PostgreSQL container for your Java tests.

What is TestContainers ?

Testcontainers is an open source framework for running lightweight instances of Container Images as part of your Tests.

By utilizing Testcontainers, the need to set up complex environments to test services becomes obsolete. You can define your test dependencies as code, effortlessly execute your tests, and watch as containers are dynamically created and disposed of. With Testcontainers’ broad language and testing framework support, all that’s required is Docker to streamline your testing process.

Setting up Testcontainers

To get started with Testcontainers, you need to include the testcontainers library as a dependency in your Java project. For Maven, add the following to your pom.xml file:

<dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>testcontainers</artifactId>
        <version>1.18.3</version>
        <scope>test</scope>
</dependency>

Additionally, include JUnit Test library to annotate your Tests. Check the following article to learn more about JUnit configuration: JUnit 5 Made Easy

Example: GenericContainer

The GenericContainer class allows you to start a Docker container with a specified image. Let’s start with a simple example of running an httpd web server using Testcontainers.

@Testcontainers
public class GenericContainerTest {



    private static final int HTTP_PORT = 80;

    @Container
    private static final GenericContainer<?> container = new GenericContainer<>("httpd:latest")
            .withExposedPorts(HTTP_PORT);

    @Test
    public void testHttpdServer() throws Exception {
        // Get the host and port of the running container
        String host = container.getHost();
        int port = container.getMappedPort(HTTP_PORT);

        // Create the URL for the HTTP request
        URL url = new URL("http://" + host + ":" + port + "/");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();

        // Send an HTTP GET request
        connection.setRequestMethod("GET");
        connection.connect();

        // Verify the response code is 200 (OK)
        int responseCode = connection.getResponseCode();
        System.out.println("Response code:"+responseCode);
        Assertions.assertEquals(200, responseCode);
    }
}

In this code snippet:

  • The @Testcontainers annotation enables Testcontainers support for the test class.
  • The @Container annotation defines a static field container that represents the GenericContainer instance.
  • The GenericContainer is created with the image name “httpd:latest” and exposes the HTTP_PORT.
  • Then, we open an HTTP connection and send an HTTP GET request to the container.
  • Finally, we verify the response code using Assertions.assertEquals() to ensure it is 200 (OK).

This code demonstrates the usage of Testcontainers to start a GenericContainer running an Apache HTTP server, perform an HTTP request, and verify the response code.

TestContainer - an example with GenericContainer

Another TestExample: A PostgreSQL Container

Besides the GenericContainer there are several pre-built API to Test Services such as Databases, Brokers or Identity Servers. As an example, we will show here how to kickstart and Test a PostgreSQL container with PostgreSQLContainer.

Firstly, we need to add to our project the dependency for PostgreSQL TestContainer and a suitable Driver for PostgreSQL:

<dependency>
	<groupId>org.testcontainers</groupId>
	<artifactId>postgresql</artifactId>
	<version>1.18.3</version>
	<scope>test</scope>
</dependency>

<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<version>42.3.1</version>
	<scope>test</scope>
</dependency>

Finally, here is our JUnit 5 Test which starts a PostgreSQL Database and executes a SELECT on the current Data when the Container is running:

@Testcontainers
public class PostgreSQLTest {

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

    private Connection connection;

    @BeforeAll
    public static void setUp() {
        postgresContainer.start();
    }

    @BeforeEach
    public void connectToDatabase() throws Exception {
        String jdbcUrl = postgresContainer.getJdbcUrl();
        String username = postgresContainer.getUsername();
        String password = postgresContainer.getPassword();

        connection = DriverManager.getConnection(jdbcUrl, username, password);
    }

    @AfterEach
    public void closeConnection() throws Exception {
        connection.close();
    }

    @Test
    public void testCurrentDateNotNull() throws Exception {
        // Execute a query to retrieve the current date from the database
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery("SELECT CURRENT_DATE");

        // Verify that the result set is not null
        assertNotNull(resultSet);

        // Move the cursor to the first row
        resultSet.next();

        // Retrieve the current date value from the result set
        LocalDate currentDate = resultSet.getObject(1, LocalDate.class);
        System.out.println("Date is " + currentDate);
        // Verify that the current date is not null
        assertNotNull(currentDate);
    }
}

As you can see, the code is much leaner as we reference the org.testcontainers.containers.PostgreSQLContainer which provides details about the JDBC Connection so that we can run a simple Test with it.

Run the above Test in your IDE and verify that the current Data is not null:

TestContainer step by step tutorial

Conclusion

In this tutorial, we explored how to use Testcontainers to set up a GenericContainer and a PostgreSQL container for your Java tests. We learned how to include Testcontainers as a dependency, start and stop containers, and perform tests using the containerized dependencies. Testcontainers is a powerful library that simplifies testing by providing disposable containers for your dependencies, enabling you to write more reliable and isolated tests.

By utilizing Testcontainers in your Java tests, you can ensure that your applications are thoroughly tested against real dependencies, leading to more robust and reliable software.

Source code for this article: https://github.com/fmarchioni/mastertheboss/tree/master/test/testcontainer

Found the article helpful? if so please follow us on Socials