@Testcontainers 는 JUnit 5 (주피터) 의 테스트컨테이너 어노테이션이다.

 

이 어노테이션은 클래스에 지정하며, 이 어노테이션이 지정된 클래스는 테스트 실행 시 자신 안에서 @Container 가 지정된 필드를 찾아 컨테이너의 라이프싸이클을 관리한다.

 

@Container 필드가 static 이면 이 컨테이너는 첫 번째 테스트 메서드 시작 전에 한 번만 시작되어 마지막 테스트 메서드 종료 시 정지된다. 즉 모든 테스트 케이스에 대해 하나의 컨테이너를 공유해서 사용한다. (이 때 jdbc:tc:.. 를 사용하면 안 된다.)

오해하지 말아야 할 것이, '첫 번째 테스트 메서드' 는 하나의 테스트 클래스 안에서의 이야기이다. 프로젝트의 모든 테스트를 수행할 때는 여러 테스트 클래스를 실행하게 되는데, 컨테이너는 한 테스트 클래스의 시작과 함께 종료되고, 다른 테스트 클래스가 있으면 다시 시작된다.

그리고 컨테이너의 라이프싸이클(시작-종료) 는 알아서 관리되기 때문에, 사용자가 직접 start() 등을 호출할 필요가 없다.

 

또, MySQL 등의 컨테이너는 기본적으로 랜덤 포트를 사용하기 때문에 이 포트를 알기 위해서는 스프링 어플리케이션 컨텍스트 시작 이벤트를 받아서 getJdbcUrl() 등을 호출하게 되는데, 스프링 어플리케이션 컨텍스트는 테스트 시 기본적으로 한 번 로드되고 재사용된다.

이렇게되면 컨테이너는 테스트 클래스마다 다시 시작되어 포트가 변경되는데, jdbc url 은 처음 테스트에서 얻은 것을 그대로 사용하게 되어 첫 테스트 클래스만이 db 커넥션 연결에 성공하고 나머지는 실패하게 된다. 이를 방지하기 위한 방법 중 하나로, @DirtiesContext 를 사용하여 매 테스트 클래스마다 새로운 어플리케이션 컨텍스트를 로드하게 하여 매번 새로운 컨테이너의 jdbc url 을 받아서 사용하는 것이 있다.

 

@Container 필드가 인스턴스 필드이면 이 컨테이너는 각 테스트 메서드 시작 전에 시작되고, 메서드 종료 시 정지된다. 모든 테스트 케이스에 대해 각각의 컨테이너가 할당되는 셈이다.

 

예 (주피터 javadoc 에서 가져옴): 

  @Testcontainers
  class MyTestcontainersTests {
 
      // will be shared between test methods
      @Container
      private static final MySQLContainer MY_SQL_CONTAINER = new MySQLContainer();
 
      // will be started before and stopped after each test method
      @Container
      private PostgreSQLContainer postgresqlContainer = new PostgreSQLContainer()
              .withDatabaseName("foo")
              .withUsername("foo")
              .withPassword("secret");
 
      @Test
      void test() {
          assertTrue(MY_SQL_CONTAINER.isRunning());
          assertTrue(postgresqlContainer.isRunning());
      }
  }

 

@Testcontainers 를 슈퍼클래스에 지정하고 테스트 클래스는 이 클래스를 상속받아 사용하는 것이 가능하다. (@Inherited)

 

출처: https://javadoc.io/doc/org.testcontainers/junit-jupiter/latest/org/testcontainers/junit/jupiter/Testcontainers.html

'Java' 카테고리의 다른 글

JPA] @OneToOne lazy loading  (0) 2022.08.22
JPA] Hibernate 1차 캐시  (0) 2022.06.27

+ Recent posts