1. testcontainer 개요
testcontainer는 테스트 시에만 사용할 수 있는 컨테이너를 실행시켜준다. 애플리케이션 실행에는 필요없지만 테스트만을 위해 컨테이너가 필요한 경우 해당 이미지를 구동시켜주는 것이다. 만약 테스트를 위해 MySQL DB가 필요하다면 testcontainer가 없는 경우 SpringBoot가 제공하는 In-memory DB를 이용하거나 따로 DB용 컨테이너를 실행해야할텐데, testcontainer는 그러한 과정 대신 테스트가 실행될 때만 MySQL용 컨테이너를 띄워주고 테스트가 끝나면 해당 컨테이너를 종료시킨다.
build.gradle에 아래처럼 추가해준다.
dependencies {
...
testImplementation "org.testcontainers:mysql:1.19.8"
testImplementation "org.testcontainers:kafka:1.19.8"
testImplementation "org.testcontainers:testcontainers:1.19.8"
testImplementation 'org.testcontainers:junit-jupiter:1.19.8'
}
MySQL과 Kafka를 사용하기 위해서 따로 추가해주면 된다.
이외에도 redis 등 많은 모듈을 지원한다. 공식 홈페이지의 Docs -> Java -> Modules 부분을 보면 어떤 컨테이너들이 제공되는지 확인할 수 있다.
https://java.testcontainers.org/
2. testcontainer 사용법
통합 테스트를 예시로 든다. @ExtendWith를 사용하여 Extension으로 정의한 클래스들을 불러오면 된다. 그리고나서 실행하면 docker desktop이나 CLI 도구로 컨테이너가 실행되는 것을 확인할 수 있다.
@SpringBootTest
@ExtendWith({KafkaExtension.class, RedisExtension.class})
class KafkaConsumerTest {
...
}
Extension 클래스들은 예를 들어 아래와 같이 만들면 된다. 상세한 옵션들은 공식 홈페이지 docs를 참고하면 된다.
public class MySQLExtension implements BeforeAllCallback, AfterAllCallback {
private MySQLContainer<?> mysqlContainer;
@Override
public void beforeAll(ExtensionContext context) {
mysqlContainer =
new MySQLContainer<>("mysql:8")
.withDatabaseName("yourDataBaseName")
.withUsername("yourUserName")
.withPassword("yourPassword");
mysqlContainer.start();
System.setProperty("spring.datasource.url", mysqlContainer.getJdbcUrl());
System.setProperty("spring.datasource.username", mysqlContainer.getUsername());
System.setProperty("spring.datasource.password", mysqlContainer.getPassword());
}
@Override
public void afterAll(ExtensionContext context) {
if (mysqlContainer != null) {
mysqlContainer.stop();
}
}
}
beforeAll과 afterAll로 container가 실행되기 전, 후에 어떻게 작동할지를 설정해줄 수 있다.
3. 추가 내용
기본적인 것은 알았으니 추가적인 내용들은 공부하면 된다. 경험상 배운 부분들을 기록해놓는다.
- MySQL의 경우 기본적으로 싱글톤으로 뜬다.
여러 테스트에 MySQLExtension을 추가하고 실행해보면 컨테이너가 1개만 뜬 채로 테스트들이 진행되는 것을 볼 수 있다. 이렇게 하면 하나의 테스트에서 DB에 데이터를 insert하는 경우 DB 자체가 오염되어 있게 되므로 다른 테스트에 영향을 줄 수도 있다. 테스트간 데이터 격리를 위해서 MySQL을 테스트마다 따로 띄우는게 필요할 수 있다.
테스트마다 MySQL 컨테이너를 따로 실행하는 경우, 당연히 컨테이너 구동 시간이 추가됨에 따라 테스트 시간 및 빌드 시간이 길어질 수 있다는 단점을 알고 있어야한다. 그리고 스프링은 Spring Context를 하나만 실행해서 모든 테스트에 공통으로 적용하는데, DB가 종료되는 경우 Spring Context에서 DB의 connection pool을 잡지 못해서 Connection 에러가 날수도 있다.
이 경우 위 글처럼 @DirtiesContext를 적용하라고 하는데, Spring Context를 바꿔주는 옵션이라 테스트가 더 느려질 수도 있다.
-tc 설정
테스트용 profile 설정에 datasource에 testcontainer를 활용하는 driver-class-name을 지정하고, url에 tc라는 문구를 추가해주면 테스트마다 @ExtendWith 내에 MySQLExtension을 따로 지정해줄 필요가 없다.
spring:
config:
activate:
on-profile: "test-profile"
jpa:
...
datasource:
driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
url: jdbc:tc:mysql:8://yourDbSchema
username: yourUserName
password: yourPassword
'Programming-[Backend] > SpringBoot' 카테고리의 다른 글
Mapstruct 기능 기록 uses, expression, @Named, qualifiedByName (0) | 2024.05.26 |
---|---|
@SpringBootApplication 이해하기 (0) | 2022.06.02 |
[탐험]Spring MVC, String 전체 앞 뒤 공백 제거하기! - MappingJackson2HttpMessageConverter, @JsonFormat, StringTrimmerEditor (0) | 2022.03.02 |
[TIL][Spring security] hasRole 적용 시 access is denied 문제 (0) | 2022.01.20 |
[TIL][링크] MapStruct 라이브러리 - @AfterMapping (0) | 2021.12.08 |