Bird
Raised Fist0
JUnittesting~5 mins

Test containers for database testing in JUnit

Choose your learning style10 modes available
Introduction

Test containers let you run a real database in a temporary environment during tests. This helps check your code works with a real database without needing a permanent setup.

You want to test database queries without changing your main database.
You need a clean database for each test to avoid data conflicts.
You want to test your code on different database versions easily.
You want to run tests on any machine without installing a database.
You want to catch database-related bugs early during development.
Syntax
JUnit
public class MyDatabaseTest {
    @Container
    public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15.3")
        .withDatabaseName("testdb")
        .withUsername("user")
        .withPassword("pass");

    @BeforeAll
    public static void setup() {
        // Setup code to connect to the container database
    }

    @Test
    public void testQuery() {
        // Your test code here
    }
}

Use the @Container annotation to manage the lifecycle of the test container.

Testcontainers automatically starts and stops the database container for your tests.

Examples
This example starts a PostgreSQL container and checks if it is running.
JUnit
public class SimplePostgresTest {
    @Container
    public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15.3");

    @Test
    public void testConnection() {
        assertTrue(postgres.isRunning());
    }
}
This example shows how to customize the database name and credentials.
JUnit
public class CustomDbTest {
    @Container
    public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15.3")
        .withDatabaseName("mydb")
        .withUsername("admin")
        .withPassword("secret");

    @Test
    public void testDbName() {
        assertEquals("mydb", postgres.getDatabaseName());
    }
}
Sample Program

This test starts a PostgreSQL container, creates a table, inserts data, and checks the number of rows.

JUnit
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import static org.junit.jupiter.api.Assertions.*;

@Testcontainers
public class DatabaseTest {

    @Container
    public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15.3")
        .withDatabaseName("testdb")
        .withUsername("user")
        .withPassword("pass");

    private static Connection connection;

    @BeforeAll
    public static void setUp() throws Exception {
        String url = postgres.getJdbcUrl();
        String username = postgres.getUsername();
        String password = postgres.getPassword();
        connection = DriverManager.getConnection(url, username, password);

        try (Statement stmt = connection.createStatement()) {
            stmt.execute("CREATE TABLE IF NOT EXISTS items (id SERIAL PRIMARY KEY, name VARCHAR(255));");
            stmt.execute("INSERT INTO items (name) VALUES ('apple'), ('banana');");
        }
    }

    @Test
    public void testItemCount() throws Exception {
        try (Statement stmt = connection.createStatement()) {
            ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM items;");
            assertTrue(rs.next());
            int count = rs.getInt(1);
            assertEquals(2, count);
        }
    }
}
OutputSuccess
Important Notes

Testcontainers require Docker to be installed and running on your machine.

Each test container runs isolated, so tests do not affect each other.

Use @Testcontainers annotation on the test class to enable container support.

Summary

Test containers let you test with real databases easily and safely.

They start and stop databases automatically during tests.

This helps catch database issues early without manual setup.

Practice

(1/5)
1. What is the main advantage of using Testcontainers for database testing in JUnit?
easy
A. It provides a real database environment isolated for each test run.
B. It replaces the need for any database in tests with mocks.
C. It automatically writes test cases for database queries.
D. It speeds up tests by using in-memory databases only.

Solution

  1. Step 1: Understand what Testcontainers provide

    Testcontainers run real database instances inside containers, giving a fresh environment for each test.
  2. Step 2: Compare with other options

    Mocks or in-memory databases do not provide real database behavior, and Testcontainers do not auto-generate tests.
  3. Final Answer:

    It provides a real database environment isolated for each test run. -> Option A
  4. Quick Check:

    Real isolated DB environment = A [OK]
Hint: Testcontainers run real DBs isolated per test [OK]
Common Mistakes:
  • Thinking Testcontainers use mocks instead of real DBs
  • Confusing Testcontainers with in-memory databases
  • Believing Testcontainers auto-generate tests
2. Which of the following is the correct way to start a PostgreSQL container using Testcontainers in a JUnit test?
easy
A. PostgreSQLContainer postgres = new PostgreSQLContainer("latest:postgres").launch();
B. PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:latest").run();
C. PostgreSQLContainer postgres = new PostgreSQLContainer("postgres").begin();
D. PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:latest").start();

Solution

  1. Step 1: Check the correct constructor and method

    The correct constructor uses the image name string and the generic type, and the container is started with start().
  2. Step 2: Validate method names and image format

    Methods like run(), begin(), or launch() do not exist. The image name format must be "postgres:latest".
  3. Final Answer:

    PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:latest").start(); -> Option D
  4. Quick Check:

    Use start() with correct image name [OK]
Hint: Use start() method with correct image name [OK]
Common Mistakes:
  • Using wrong method names like run(), begin(), or launch()
  • Incorrect image name format
  • Omitting generic type parameter
3. Given the following JUnit test snippet using Testcontainers, what will be the output of System.out.println(postgres.getJdbcUrl());?
PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:13-alpine").start();
System.out.println(postgres.getJdbcUrl());
postgres.stop();
medium
A. The literal string "postgres:13-alpine"
B. An error because getJdbcUrl() is not available
C. A JDBC URL string like jdbc:postgresql://localhost:randomPort/test
D. Null, because the container is stopped immediately

Solution

  1. Step 1: Understand what getJdbcUrl() returns

    After starting, getJdbcUrl() returns the connection URL to the running container database.
  2. Step 2: Check container lifecycle and output

    The container is started before printing, so the URL is valid. The container stops after printing, so output is not null or error.
  3. Final Answer:

    A JDBC URL string like jdbc:postgresql://localhost:randomPort/test -> Option C
  4. Quick Check:

    getJdbcUrl() after start() returns valid URL [OK]
Hint: Call getJdbcUrl() only after container start() [OK]
Common Mistakes:
  • Calling getJdbcUrl() before start()
  • Expecting literal image name as URL
  • Assuming output is null after stop() immediately
4. Identify the error in this JUnit test using Testcontainers for MySQL:
@Test
void testDbConnection() {
  MySQLContainer mysql = new MySQLContainer("mysql:8.0");
  mysql.start();
  String url = mysql.getJdbcUrl();
  // Use url to connect
  mysql.stop();
  mysql.stop();
}
medium
A. Calling mysql.stop() twice causes an error.
B. Missing generic type parameter in MySQLContainer declaration.
C. Container must be started with run() instead of start().
D. The image name "mysql:8.0" is invalid.

Solution

  1. Step 1: Check MySQLContainer declaration

    MySQLContainer is a generic class and should be declared with a wildcard: MySQLContainer<?>.
  2. Step 2: Verify other parts

    Calling stop() twice is safe (idempotent). start() is correct method. Image name "mysql:8.0" is valid.
  3. Final Answer:

    Missing generic type parameter in MySQLContainer declaration. -> Option B
  4. Quick Check:

    Use MySQLContainer to avoid raw type warning [OK]
Hint: Always use generic type with Testcontainers classes [OK]
Common Mistakes:
  • Calling stop() multiple times causes error
  • Using run() instead of start()
  • Incorrect image name format
5. You want to write a JUnit test that uses a PostgreSQL Testcontainer and runs a SQL script to initialize the database before tests. Which approach correctly achieves this?
hard
A. Use withInitScript("init.sql") on the PostgreSQLContainer instance before starting it.
B. Manually connect after start() and run the SQL script using JDBC in each test method.
C. Include the SQL script inside the Docker image and use a custom image name.
D. Start the container, then copy the SQL file into the container using copyFileToContainer() and run it manually.

Solution

  1. Step 1: Identify Testcontainers feature for init scripts

    Testcontainers provide withInitScript() method to run SQL scripts automatically on container startup.
  2. Step 2: Compare alternatives

    Manual JDBC execution or copying files is more complex and error-prone. Custom images are possible but not necessary for simple init scripts.
  3. Final Answer:

    Use withInitScript("init.sql") on the PostgreSQLContainer instance before starting it. -> Option A
  4. Quick Check:

    Use withInitScript() for automatic DB initialization [OK]
Hint: Use withInitScript() to auto-run SQL on container start [OK]
Common Mistakes:
  • Running SQL manually in each test instead of init script
  • Modifying Docker images unnecessarily
  • Copying files manually instead of using built-in methods
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard