도커와 테스트 : Testcontainers 소개 / 설치 / 기능

YoungG209·2022년 1월 5일
0

인프런의 "더 자바, 애플리케이션을 테스트하는 다양한 방법”을 보고 정리한 것임

1. Testcontainers 소개

  • 테스트에서 도커 컨테이너를 실행할 수 있는 라이브러리 (https://www.testcontainers.org/)
  • 테스트 실행시 DB를 설정하거나 별도의 프로그램 또는 스크립트를 실행할 필요 없음
  • 보다 Production에 가까운 테스트를 만들 수 있음
  • 테스트가 느려짐

전제 조건

  • 로컬 PC에 Docker가 설치되어 있어야 함

2. Testcontainers 설치

Gradle 설정 추가

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.projectlombok:lombok:1.18.20'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testImplementation 'org.postgresql:postgresql:42.3.1'

    /* testcontainers */
    testImplementation 'org.testcontainers:postgresql:1.16.2'
    testImplementation 'org.testcontainers:junit-jupiter'
}

dependencyManagement {
    imports {
        mavenBom("org.testcontainers:testcontainers-bom:1.16.2")
    }
}

application-test.properties 추가

  • src/test/resources/application-test.properties
spring.datasource.url=jdbc:tc:postgresql:///postgres
// Spring Boot version 2.3.0 이면 추가 해야함
// spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.jpa.hibernate.ddl-auto=create-drop

Test 소스

@SpringBootTest
@ExtendWith(MockitoExtension.class)
@ActiveProfiles("test")
@Testcontainers
class TestcontainersTest {

    @Mock
    MemberService memberService;

    @Autowired
    StudyRepository studyRepository;

    @Container
    static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres").withDatabaseName("study-db");

    @BeforeAll
    static void beforeAll() {
        String jdbcUrl = postgreSQLContainer.getJdbcUrl();
        String databaseName = postgreSQLContainer.getDatabaseName();

        System.out.println("databaseName : " + databaseName + " / jdbcUrl : " + jdbcUrl);
    }

    @BeforeEach
    void beforeEach() {
        studyRepository.deleteAll();
    }

    @Test
    void createNewStudy() {
        StudyService studyService = new StudyService(memberService, studyRepository);
        assertNotNull(studyService);

        Member member = new Member();
        member.setId(1L);
        member.setEmail("lee@gamil.com");

        Study study = new Study(10, "테스트");

        when(memberService.findById(1L)).thenReturn(Optional.of(member));

        studyService.createNewStudy(1L, study);

        assertNotNull(study.getOwnerId());
        assertEquals(member.getId(), study.getOwnerId());

        verify(memberService, times(1)).notify(study);

        verifyNoMoreInteractions(memberService);
    }

    @DisplayName("다른 사용자가 볼 수 있도록 스터디를 공개한다.")
    @Test
    void openStudy() {
        // Given
        StudyService studyService = new StudyService(memberService, studyRepository);
        Study study = new Study(10, "the Java, Test");
//        given(studyRepository.save(study)).willReturn(study);

        // When
        studyService.openStudy(study);

        // Then
        assertEquals(study.getStatus(),StudyStatus.OPENED);
        assertNotNull(study.getOpenedDateTime());
        then(memberService).should().notify(study);
    }
}

3. Testcontainers 기능

어노테이션

  • @Testcontainers
    • JUnit 5 확장팩으로 테스트 클래스에 @Container를 사용한 필드를 찾아서 컨테이너 라이프사이클 관련 메소드를 실행
  • @Container
    • 인스턴스 필드에 사용하면 모든 테스트 마다 컨테이너를 재시작 하고, 스태틱 필드에 사용하면 클래스 내부 모든 테스트에서 동일한 컨테이너를 재사용

컨테이너 만들기

New GenericContainer(String imageName)

네트워크

  • withExposedPorts(int...)
    • DB port가 같다면 테스트 DB 및 운영서버 DB 중 랜덤하게 사용(충돌하지 않는 포트 우선사용)
  • getMappedPort(int)
    • 내 호스트가 어떤 포트를 사용했는지 확인

환경 변수 설정

withEnv(key, value)

명령어 실행

withCommand(String cmd...)

사용할 준비가 됐는지 확인하기

  • waitingFor(Wait)
    • 언제 서버가 가용되는데 시간이 걸리는지
  • Wait.forHttp(String url)
    • Http요청으로 응답을 확인 및 해당 컨테이너가 가용한지 확인 후 테스트 진행
  • Wait.forLogMessage(String message)
    • 특정한 로그 메세지가 출력이 되었는지 확인 후 테스트 진행

로그 살펴보기

  • getLogs()
    - 현재까지의 컨테이너의 로그들을 모두 가져오기
    -followOutput()
    - 스트리밍으로 로그를 출력
profile
고우고우~

0개의 댓글