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.
Test containers for database testing in 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.
public class SimplePostgresTest { @Container public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15.3"); @Test public void testConnection() { assertTrue(postgres.isRunning()); } }
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()); } }
This test starts a PostgreSQL container, creates a table, inserts data, and checks the number of rows.
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); } } }
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.
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
Testcontainers for database testing in JUnit?Solution
Step 1: Understand what Testcontainers provide
Testcontainers run real database instances inside containers, giving a fresh environment for each test.Step 2: Compare with other options
Mocks or in-memory databases do not provide real database behavior, and Testcontainers do not auto-generate tests.Final Answer:
It provides a real database environment isolated for each test run. -> Option AQuick Check:
Real isolated DB environment = A [OK]
- Thinking Testcontainers use mocks instead of real DBs
- Confusing Testcontainers with in-memory databases
- Believing Testcontainers auto-generate tests
Solution
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 withstart().Step 2: Validate method names and image format
Methods likerun(),begin(), orlaunch()do not exist. The image name format must be "postgres:latest".Final Answer:
PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:latest").start(); -> Option DQuick Check:
Usestart()with correct image name [OK]
start() method with correct image name [OK]- Using wrong method names like run(), begin(), or launch()
- Incorrect image name format
- Omitting generic type parameter
System.out.println(postgres.getJdbcUrl());?
PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:13-alpine").start();
System.out.println(postgres.getJdbcUrl());
postgres.stop();Solution
Step 1: Understand what getJdbcUrl() returns
After starting,getJdbcUrl()returns the connection URL to the running container database.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.Final Answer:
A JDBC URL string like jdbc:postgresql://localhost:randomPort/test -> Option CQuick Check:
getJdbcUrl() after start() returns valid URL [OK]
- Calling getJdbcUrl() before start()
- Expecting literal image name as URL
- Assuming output is null after stop() immediately
@Test
void testDbConnection() {
MySQLContainer mysql = new MySQLContainer("mysql:8.0");
mysql.start();
String url = mysql.getJdbcUrl();
// Use url to connect
mysql.stop();
mysql.stop();
}Solution
Step 1: Check MySQLContainer declaration
MySQLContainer is a generic class and should be declared with a wildcard:MySQLContainer<?>.Step 2: Verify other parts
Callingstop()twice is safe (idempotent).start()is correct method. Image name "mysql:8.0" is valid.Final Answer:
Missing generic type parameter in MySQLContainer declaration. -> Option BQuick Check:
Use MySQLContainer to avoid raw type warning [OK]
- Calling stop() multiple times causes error
- Using run() instead of start()
- Incorrect image name format
Solution
Step 1: Identify Testcontainers feature for init scripts
Testcontainers providewithInitScript()method to run SQL scripts automatically on container startup.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.Final Answer:
Use withInitScript("init.sql") on the PostgreSQLContainer instance before starting it. -> Option AQuick Check:
Use withInitScript() for automatic DB initialization [OK]
- Running SQL manually in each test instead of init script
- Modifying Docker images unnecessarily
- Copying files manually instead of using built-in methods
Ready to prove it?
Pass the test and this pattern moves unknown
