Skip to content
Fresh

Lifecycle callbacks

Back

Guides

Testcontainers container lifecycle management using JUnit 5

Learn different approaches to manage container lifecycle with Testcontainers using JUnit 5 lifecycle callbacks, extension annotations, and the singleton containers pattern.

Java Testing with Docker

20 minutes

1

Create the project

2

Lifecycle callbacks

3

Extension annotations

4

Singleton containers

« Back to all guides

JUnit 5 lifecycle callbacks

Copy as Markdown

Open MarkdownAsk Docs AIClaudeOpen in Claude


When testing with Testcontainers, you want to start the required containers before executing any tests and remove them afterwards. You can use JUnit 5 @BeforeAll and @AfterAll lifecycle callback methods for this:

java
package com.testcontainers.demo;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.testcontainers.postgresql.PostgreSQLContainer;

class CustomerServiceWithLifeCycleCallbacksTest {

  static PostgreSQLContainer postgres = new PostgreSQLContainer(
    "postgres:16-alpine"
  );

  CustomerService customerService;

  @BeforeAll
  static void startContainers() {
    postgres.start();
  }

  @AfterAll
  static void stopContainers() {
    postgres.stop();
  }

  @BeforeEach
  void setUp() {
    customerService =
    new CustomerService(
      postgres.getJdbcUrl(),
      postgres.getUsername(),
      postgres.getPassword()
    );
    customerService.deleteAllCustomers();
  }

  @Test
  void shouldCreateCustomer() {
    customerService.createCustomer(new Customer(1L, "George"));

    Optional<Customer> customer = customerService.getCustomer(1L);
    assertTrue(customer.isPresent());
    assertEquals(1L, customer.get().id());
    assertEquals("George", customer.get().name());
  }

  @Test
  void shouldGetCustomers() {
    customerService.createCustomer(new Customer(1L, "George"));
    customerService.createCustomer(new Customer(2L, "John"));

    List<Customer> customers = customerService.getAllCustomers();
    assertEquals(2, customers.size());
  }
}

Here's what the code does:

  • PostgreSQLContainer is declared as a static field. The container starts before all tests and stops after all tests in this class.
  • @BeforeAll starts the container, @AfterAll stops it.
  • @BeforeEach initializes CustomerService with the container's JDBC parameters and deletes all rows to give each test a clean database.

Key observations:

  • Because the container is a static field, it's shared across all test methods in the class. You can declare it as a non-static field and use @BeforeEach/@AfterEach to start a new container per test, but this isn't recommended as it's resource-intensive.
  • Even without explicitly stopping the container in @AfterAll, Testcontainers uses the Ryuk container to clean up containers automatically when the JVM exits.

JUnit 5 extension annotations »