Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

개발자일기

도커를 이용한 테스트 본문

개발

도커를 이용한 테스트

ka0oll 2020. 3. 21. 15:41

Testcontainers

JAVA 테스트에서 도커를 이용 할수있는 라이브러리

왜 도커를 사용해서 할까

  • 좀더 실제 환경에 맞는 테스트 환경을 구축
  • 별도의 환경 구축이 필요없다. 레디스, DB같은것들의 실행이 필요없다

디비 테스트를 할경우에는 H2메모리 디비를 쓰면 되자 않나?

  • 디비경우 디비마다 트랜잭션 기본 설정같은게 다르니, 실제 환경이랑 다를수 있다.

그러면 단점은?

  • 테스트의 속도가 느려진다. 컨테이너를 실행, 종료의 시간이 다ㅗㅅ 걸림

도커 컨테이너 디비를 이용한 테스트 시작하기

JUnit과 함께 : https://www.testcontainers.org/test_framework_integration/junit_5/

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>1.13.0</version>
    <scope>test</scope>
</dependency>

@Testcontainers
JUnit 5 확장팩으로 테스트 클래스에 @Container를 사용한 필드를 찾아서 컨테이너 라이프사이클 관련 메소드를 실행해준다.

 

@Container
인스턴스 필드에 사용하면 모든 테스트 마다 컨테이너를 재시작 하고, 스태틱 필드에 사용하면 클래스 내부 모든 테스트에서 동일한 컨테이너를 재사용한다.

 

테스트에 필요한 도커 모듈은 직접 import : MYSQL 모듈

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>mysql</artifactId>
    <version>1.13.0</version>
    <scope>test</scope>
</dependency>
@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 기능들

컨테이너 만들기
New GenericContainer(String imageName)

 

네트워크
withExposedPorts(int...)
getMappedPort(int)

 

환경 변수 설정
withEnv(key, value)

 

명령어 실행
withCommand(String cmd...)

 

사용할 준비가 됐는지 확인하기
waitingFor(Wait)
Wait.forHttp(String url)
Wait.forLogMessage(String message)

 

로그 살펴보기
getLogs()
followOutput()

스프링 설정과 연동

스프링의 property를 도커컨테이너의 사용한값으로 오버라이딩 해줘야한다.

전체 흐름

  1. Testcontainer를 사용해서 컨테이너 생성
  2. ApplicationContextInitializer를 구현하여 생선된 컨테이너에서 정보를 축출하여 Environment에 넣어준다.
  3. @ContextConfiguration을 사용해서 ApplicationContextInitializer 구현체를 등록한다.
  4. 테스트 코드에서 Environment, @Value, @ConfigurationProperties 등 다양한 방법으로 해당 프로퍼티를 사용한다.

docker compose

docker compose모듈
https://www.testcontainers.org/modules/docker_compose/
https://github.com/palantir/docker-compose-rule

더 자바, "코드를 테스트 하는 다양한 방법" 샘플 코드
docker compose + 스프링 설정과 연동

@SpringBootTest
@ExtendWith(MockitoExtension.class)
@ActiveProfiles("test")
@Testcontainers
@Slf4j
@ContextConfiguration(initializers = StudyServiceTest.ContainerPropertyInitializer.class)
class StudyServiceTest {

    @Mock MemberService memberService;

    @Autowired StudyRepository studyRepository;

    @Value("${container.port}") int port;

    @Container
    static DockerComposeContainer composeContainer =
            new DockerComposeContainer(new File("src/test/resources/docker-compose.yml"))
            .withExposedService("study-db", 5432);

    @Test
    void createNewStudy() {
        System.out.println("========");
        System.out.println(port);

        // Given
        StudyService studyService = new StudyService(memberService, studyRepository);
        assertNotNull(studyService);

        Member member = new Member();
        member.setId(1L);
        member.setEmail("keesun@email.com");

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

        given(memberService.findById(1L)).willReturn(Optional.of(member));

        // When
        studyService.createNewStudy(1L, study);

        // Then
        assertEquals(1L, study.getOwnerId());
        then(memberService).should(times(1)).notify(study);
        then(memberService).shouldHaveNoMoreInteractions();
    }

    @DisplayName("다른 사용자가 볼 수 있도록 스터디를 공개한다.")
    @Test
    void openStudy() {
        // Given
        StudyService studyService = new StudyService(memberService, studyRepository);
        Study study = new Study(10, "더 자바, 테스트");
        assertNull(study.getOpenedDateTime());

        // When
        studyService.openStudy(study);

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

    static class ContainerPropertyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

        @Override
        public void initialize(ConfigurableApplicationContext context) {
            TestPropertyValues.of("container.port=" + composeContainer.getServicePort("study-db", 5432))
                    .applyTo(context.getEnvironment());
        }
    }

}

'개발' 카테고리의 다른 글

Mockito를 이용한 Unit테스트 Mocking  (0) 2020.03.22
Spring의 핵심 및 기술  (0) 2020.03.21
JUnit5을 이용한 단위테스트  (0) 2020.03.21
MSA 마이크로아키텍처 알아보자(1)  (1) 2020.03.21
코드를 조작하는 방법  (0) 2020.03.21
Comments