이 글은 온라인 강의를 듣고 해당 내용을 직접 실습해보며 정리한 글입니다. 더 자세한 내용은 아래 강의에서 확인할 수 있습니다.
더 자바, 애플리케이션을 테스트하는 다양한 방법 - 인프런 | 강의(백기선)
테스트에서 도커 컨테이너를 실행할 수 있는 라이브러리
- 테스트 실행시 DB를 설정하거나 별도의 프로그램 또는 스크립트를 실행할 필요 없다.
- 보다 Production에 가까운 테스트를 만들 수 있다.
- 테스트가 느려진다.
- https://www.testcontainers.org/
→ 테스트컨테이너를 쓰면 테스트용 도커를 만들 필요도, 손수 띄우고 내릴 필요도 없다.
설치하기
디펜던시
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.15.1</version>
<scope>test</scope>
</dependency>
junit-jupiter를 지원하는 testcontainers
독스에 설치할 컨테이너 별로 디펜던시를 확인할 수 있다.
ex) postgresql 전용 디펜던시로 postgres 컨테이너를 띄울 수 있다.
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.15.1</version>
<scope>test</scope>
</dependency>
테스트 컨테이너 선언하기
@SpringBootTest
@ActiveProfiles("test")
@Slf4j
class StudyServiceMockContainerTest {
static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.12")
.withDatabaseName("studytest")
;
@BeforeAll
static void beforeAll() {
postgreSQLContainer.start();
log.info(postgreSQLContainer.getJdbcUrl()); // jdbc:postgresql://localhost:32769/test?loggerLevel=OFF
}
@AfterAll
static void afterAll() {
postgreSQLContainer.stop();
}
}
- static PostgreSQLContainer postgreSQLContainer : static 은 안 붙이면 각 @Test 마다 컨테이너가 재시작된다. static을 붙이면 모든 @Test 에서 컨테이너를 재사용할 수 있다.
- @BeforeAll, @AfterAll : 테스트 클래스 시작 전에 컨테이너를 올려주고 모든 테스트가 끝나면 컨테이너를 내려준다.
테스트 컨테이너 선언하기 - 좀 더 간단하게
@SpringBootTest
@Testcontainers // 확장체 지원하는 composed annotation
class StudyServiceMockContainerTest {
@Container
static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.12")
.withDatabaseName("studytest")
; // 모든 테스트에서 공유하기 위해 static 선언
}
- @Testcontainers: @Container 가 붙은 컨테이너 객체를 찾아 라이프사이클 관리 메소드를 실행 해준다.
- @Container: 컨테이너임을 알리는 표식. static 필드면 모든 테스트에 같은 컨테이너를 재사용하고, static 이 아니면 각 테스트마다 컨테이너를 재시작한다.
테스트컨테이너가 띄운 컨테이너를 스프링 설정파일에서 알아차리게 하는 법
application-test.properties
spring.datasource.url=jdbc:tc:postgresql:///studytest
spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriver
1. url에 jdbc 뒤에 tc를 붙여준다.
2.textcontainers 가 제공하는 드라이버를 지정한다.
testcontainer 관점에서 ip port 는 중요하지 않다. jdbc:mysql:5.7.34://localhost:3306/databasename 와 jdbc:mysql:5.7.34:///databasename 를 같은 URI 로 인식한다.
ContainerDatabaseDriver 가 jdbc:tc: 가 포함된 URI 를 찾아 알맞게 커넥션 시켜준다.
Container 기능 살펴보기
※ PostgreSQLContainer 처럼 특정 서비스에 종속된 Container 가 아닌 이미지명만으로 Container 생성하기
GenericContainer 생성 - https://www.testcontainers.org/features/creating_container/
@Container
static GenericContainer container = new GenericContainer("postgres:9.6.12");
로컬에서 해당 이미지를 찾고 없으면 public 저장소에서 이미지를 다운받는다.
Container 옵션 설정
@Container
static GenericContainer container = new GenericContainer("postgres:9.6.12")
.withExposedPorts(5432)
.withEnv("POSTGRES_DB", "studytest")
.waitingFor(Wait.forListeningPort())
.waitingFor(Wait.forHttp("/hello"))
.waitingFor(Wait.forLogMessage("regex", 1))
;
- .withExposedPorts(5432) : 컨테이너 내에서 사용할 포트 지정
- .withEnv("POSTGRES_DB", "studytest") : 환경변수 설정
Container 정보 확인
@BeforeAll
static void beforeAll() {
log.info("host port: {}", container.getMappedPort(5432));
log.info("getLogs(): {}", container.getLogs());
container.followOutput(new Slf4jLogConsumer(log));
}
- container.getMappedPort(5432) : 컨테이너 포트 5432 에 바인딩된 호스트 포트 확인
- container.getLogs() : 컨테이너 안 로그 string 으로 받기
- container.followOutput(new Slf4jLogConsumer(log)) : 로그 streaming 으로 받기
Container 정보를 스프링 프로퍼티로 등록하기
: 스프링 컨텍스트에 컨테이너 정보를 프로퍼티로 넣고 스프링을 통해 프로퍼티 접근하기
@SpringBootTest
@ContextConfiguration(initializers = StudyServiceMockContainerTest.ContainerPropertyInitializer.class)
@Testcontainers
class StudyServiceMockContainerTest {
@Autowired
Environment environment;
@Value("${container.port}")
int port;
@Container
static GenericContainer container = new GenericContainer("postgres:9.6.12")// 지원하는 클래스 없을 때 GenericContainer 로 생성 가능. 로컬에서 이미지 찾고 없으면 땡겨온다.
.withExposedPorts(5432) // 컨테이너 내 포트
;
@BeforeEach
void beforeEach() {
log.info("host port by spring environment: {}", environment.getProperty("container.port"));
log.info("host port by spring value: {}", port);
}
@Test
public void createNewStudy() {
}
static class ContainerPropertyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertyValues.of("container.port=" + container.getMappedPort(5432))
.applyTo(applicationContext.getEnvironment())
;
}
}
}
- ContainerPropertyInitializer : 프로퍼티 추가
- ApplicationContextInitializer : 이 인터페이스를 구현함으로써 프로퍼티를 추가할 수 있다. 스프링부트가 아닌 스프링 코어에서 제공하는 콜백 인터페이스. ApplicationContext 를 초기화하는 과정에서 실행
- @ContextConfiguration : 스프링이 테스트 컨텍스트 만들 때 우리가 만든 ContainerPropertyInitializer 도 추가할 수 있도록 설정해줌
Docker-compose 사용하기
docker-compose 사용하면: 도커 컨테이너 동시에 띄울 때 그들간의 관계, 순서 등을 정의할 수 있고 같이 관리할 수 있다.
Docker-compose container 띄우기
@Container
static DockerComposeContainer dockerComposeContainer = new DockerComposeContainer(new File("src/test/resources/docker-compose.yml"));
docker-compose.yml
version: "3"
services:
study-db:
image: postgres
ports:
- 5432
environment:
POSTGRES_DB: study
POSTGRES_USER: study
POSTGRES_PASSWORD: study
디펜던시 1.12.4 부터 docker-compose.yml 형식에 맞게 정상작동한다.
docker-compose는 느리기 때문에 다 띄워져야 테스트를 실행할 수 있도록 wait을 걸어주는 것이 좋다.
Docker-compose Container 정보를 스프링 프로퍼티로 등록하기
@SpringBootTest
@ContextConfiguration(initializers = StudyServiceMockContainerTest.ContainerPropertyInitializer.class)
@Testcontainers
class StudyServiceMockContainerTest {
@Autowired
Environment environment;
@Value("${container.port}")
int port;
@Container
static DockerComposeContainer dockerComposeContainer = new DockerComposeContainer(new File("src/test/resources/docker-compose.yml"))
.withExposedService("study-db", 5432)
;
@BeforeEach
void beforeEach() {
log.info("host port by spring environment: {}", environment.getProperty("container.port"));
log.info("host port by spring value: {}", port);
}
@Test
public void createNewStudy() {
}
static class ContainerPropertyInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertyValues.of("container.port=" + dockerComposeContainer.getServicePort("study-db", 5432))
.applyTo(applicationContext.getEnvironment())
;
}
}
}
- .withExposedService("study-db", 5432) : 특정서비스를 expose 해줘야한다.
TestContainers 설명
글 읽어주셔서 언제나 감사합니다. 좋은 피드백, 개선 피드백 너무나도 환영합니다.
연관글
2022.06.21 - [프로그래머/Java] - Java 에서 테스트용 도커 컨테이너 띄우는 법 : TestContainer
'SearchDeveloper > 애플리케이션테스트' 카테고리의 다른 글
[Postman] Runner 와 newman 으로 response 파일에 쓰기 (0) | 2022.07.24 |
---|---|
Junit5 애노테이션 설명 (0) | 2022.07.24 |
Junit5 Assertions / Assumptions 설명 (0) | 2022.07.24 |
Mockito 설명 (0) | 2022.07.24 |