Jupiter / JUnit 5 확장으로 컨테이너 라이프사이클 관리하기

Jupiter / JUnit 5 확장으로 컨테이너 라이프사이클 관리하기

테스트 클래스마다 컨테이너를 직접 띄우고 내리는 코드를 반복하다 보면 테스트 프레임워크와 자연스럽게 통합되고 싶어져요. Testcontainers의 JUnit Jupiter 확장을 쓰면 @Testcontainers@Container 애노테이션 하나로 컨테이너의 시작·공유·정리를 프레임워크에 맡길 수 있어요. 이 페이지에서는 컨테이너를 테스트 메서드마다 다시 시작하거나 클래스 안에서 공유하는 두 가지 모드를 살펴볼게요.

출처: 공식문서 — Jupiter / JUnit 5

본문

이 모듈은 JUnit Jupiter 확장 모델을 기반으로 한 API를 제공해요.

이 확장은 두 가지 모드를 지원해요.

  • 테스트 메서드마다 다시 시작되는 컨테이너
  • 테스트 클래스의 모든 메서드가 공유하는 컨테이너

Jupiter/JUnit 5 통합은 별도의 라이브러리 JAR로 패키징되어 있어요. 자세한 내용은 아래를 참고하세요.

확장

Jupiter 통합은 @Testcontainers 애노테이션으로 제공돼요.

이 확장은 @Container로 애노테이션된 모든 필드를 찾아서 그 컨테이너의 라이프사이클 메서드(Startable 인터페이스의 메서드)를 호출해요. static 필드로 선언된 컨테이너는 테스트 메서드 사이에서 공유돼요. 이 컨테이너는 어떤 테스트 메서드가 실행되기 전에 딱 한 번 시작되고, 마지막 테스트 메서드가 실행된 뒤에 중지돼요. 인스턴스 필드로 선언된 컨테이너는 테스트 메서드마다 시작되고 중지돼요.

참고: 이 확장은 순차 테스트 실행에서만 테스트되었어요. 병렬 테스트 실행에서 쓰는 것은 지원되지 않으며 의도하지 않은 부작용이 있을 수 있어요.

예시:

@Testcontainers
class MixedLifecycleTests {

    // will be shared between test methods
    @Container
    private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer("mysql:8.0.36");

    // will be started before and stopped after each test method
    @Container
    private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer("postgres:9.6.12")
        .withDatabaseName("foo")
        .withUsername("foo")
        .withPassword("secret");

    @Test
    void test() {
        assertThat(MY_SQL_CONTAINER.isRunning()).isTrue();
        assertThat(postgresqlContainer.isRunning()).isTrue();
    }
}

예시

Testcontainers 확장을 쓰려면 테스트 클래스에 @Testcontainers를 붙이면 돼요.

다시 시작되는 컨테이너

다시 시작되는 컨테이너를 정의하려면 테스트 클래스 안에 인스턴스 필드를 선언하고 @Container로 애노테이션하면 돼요.

@Testcontainers
class TestcontainersNestedRestartedContainerTests {

    @Container
    private final GenericContainer<?> topLevelContainer = new GenericContainer<>(JUnitJupiterTestImages.HTTPD_IMAGE)
        .withExposedPorts(80);

    ...

    @Test
    void top_level_container_should_be_running() {
        assertThat(topLevelContainer.isRunning()).isTrue();

        ...
    }

    @Nested
    class NestedTestCase {

        @Container
        private final GenericContainer<?> nestedContainer = new GenericContainer<>(JUnitJupiterTestImages.HTTPD_IMAGE)
            .withExposedPorts(80);

        @Test
        void both_containers_should_be_running() {
            // top level container is restarted for nested methods
            assertThat(topLevelContainer.isRunning()).isTrue();
            // nested containers are only available inside their nested class
            assertThat(nestedContainer.isRunning()).isTrue();

            ...
        }
    }
}

공유 컨테이너는 최상위 테스트 클래스의 static 필드로 정의하고 @Container로 애노테이션해야 해요. 공유 컨테이너는 중첩 테스트 클래스 안에서는 선언할 수 없다는 점을 기억하세요. 중첩 테스트 클래스는 non-static으로 정의돼야 하므로 static 필드를 가질 수 없기 때문이에요.

싱글톤 컨테이너

싱글톤 컨테이너 패턴도 JUnit 5를 쓸 때 하나의 선택지라는 점을 알아 두세요.

제한 사항

이 확장은 순차 테스트 실행에서만 테스트되었어요. 병렬 테스트 실행에서 쓰는 것은 지원되지 않으며 의도하지 않은 부작용이 있을 수 있어요.

프로젝트 의존성에 JUnit 5 지원 추가하기

pom.xml/build.gradle 파일에 다음 의존성을 추가하면 돼요.

testImplementation "org.testcontainers:testcontainers-junit-jupiter:2.0.5"
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers-junit-jupiter</artifactId>
    <version>2.0.5</version>
    <scope>test</scope>
</dependency>

더 알아보기