개요
로컬 혹은 CI 서버에서 실제 운영 환경과 같은 디비를 사용하여 통합 테스트를 하고 싶어 TestContainers 설정을 적용했다.
세부 설정은 docker-compose.yml로 관리하는게 편하여 이를 import하는 방식으로 구현했다.
환경
스프링 부트 3.2.6
Gradle 8.8
자바 17
설정
gradle에 라이브러리 추가
ext {
testcontainersVersion = "1.19.0"
}
dependencies {
testImplementation "org.springframework.boot:spring-boot-testcontainers"
testImplementation 'org.testcontainers:mysql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
dependencyManagement {
imports {
mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}"
}
}
src/test/resources
ㄴ application-test.yml
ㄴ docker-compse.yml
ㄴ schema.sql
docker-compose.yml
version: "3.8"
services:
mysql:
image: mysql:8.0
environment:
MYSQL_DATABASE: container
MYSQL_CHARSET: utf8mb4
MYSQL_COLLATION: utf8mb4_unicode_ci
MYSQL_ROOT_PASSWORD: password
ports:
- 3306
volumes:
- ./schema.sql:/docker-entrypoint-initdb.d/schema.sql
networks:
- test_network
networks:
test_network:
driver: bridge
name: test_network
- 레디스 등 다른 서비스를 띄워야 한다면 일반적인 docker-compose.yml 하는 것처럼 추가하면 된다.
- DB에 DDL을 실행시키기 위해 docker volumes 설정
- 네트워크를 지정하지 않으면 TestContainer를 가동시킬때마다 네트워크를 별도로 생성하게 된다.
- 네트워크 풀이 가득찰 경우 아래와 같은 에러가 발생하여 실행이 안될수도 있으니 네트워크를 별도로 지정해주자!
14:02:57.541 [Test worker] ERROR tc.docker:24.0.2 -- Log output from the failed container: Network epceaxzqtavu_default Creating Network epceaxzqtavu_default Error failed to create network epceaxzqtavu_default: Error response from daemon: all predefined address pools have been fully subnetted
- name 까지 지정해줘야 한다. 지정해 주지 않으면 rhp9z3rirgm1_test_network 처럼 앞에 랜덤 접두사가 붙은 네트워크가 실행시 마다 종종 추가된다.
schema.sql
DROP TABLE IF EXISTS table_name;
CREATE TABLE `table_name` {}
- 필요한 DDL을 넣어 주자
IntegrationTest.java
@ActiveProfiles("test")
@SpringBootTest
@ContextConfiguration(initializers = IntegrationTest.IntegrationTestInitializer.class)
public class IntegrationTest {
private static final ComposeContainer DOCKER_COMPOSE =
new ComposeContainer(new File("src/test/resources/docker-compose.yml"))
.withExposedService("mysql", 3306, Wait.forLogMessage(".*mysqld: ready for connections.*", 2));
@BeforeAll
public static void setupContainers() {
DOCKER_COMPOSE.start();
}
static class IntegrationTestInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(@NotNull ConfigurableApplicationContext applicationContext) {
Map<String, String> properties = new HashMap<>();
setDatabaseProperties(properties);
TestPropertyValues.of(properties).applyTo(applicationContext);
}
private void setDatabaseProperties(Map<String, String> properties) {
String rdbmsHost = DOCKER_COMPOSE.getServiceHost("mysql", 3306);
Integer rdbmsPort = DOCKER_COMPOSE.getServicePort("mysql", 3306);
properties.put("spring.datasource.url", "jdbc:mysql://" + rdbmsHost + ":" + rdbmsPort + "/container");
properties.put("spring.datasource.username", "root");
properties.put("spring.datasource.password", "password");
}
}
}
- docker-compose.yml 가져오고 스프링 컨텍스트를 띄우기 전에 디비 설정 해준다음, TestContainers를 실행
private static final ComposeContainer DOCKER_COMPOSE =
new ComposeContainer(new File("src/test/resources/docker-compose.yml"))
.withExposedService("mysql", 3306, Wait.forLogMessage(".*mysqld: ready for connections.*", 2));
- 위 코드가 핵심이다. 처음에 DockerComposeContainer으로 했다가 에러가 떠서 ComposeContainer으로 변경했다. 내부 코드 까보니 DockerComposeContainers는 @Deprecated 되었다.
- 컨테이너가 뜬 다음 테스트 코드가 실행 되야 하기 때문에 Wait을 설정해줘야 한다. 안그럼 실행을 할 수 없다. mysql 메시지 내용 찾느라 조금 헤멤..
- 다른 서비스도 띄워야 한다면 .withExposedService 한줄 추가하고 IntegrationTestInitializer에서 해당 서비스 설정을 추가하면 된다.
public class DatabaseTests extends IntegrationTest {
@Autowired
private TestJpaRepository testJpaRepository;
@Test
void test() {
TestEntity save = testJpaRepository.save(TestEntity.create(1L));
}
}
- 생성한 DDL에 맞는 엔티티 하나를 가지고 간단히 삽입 테스트를 했을때 에러가 뜨지 않으면 성공
추후 레디스 같은 다른 서비스도 추가하게 되면 글 내용을 수정하겠다!!
참고
https://java.testcontainers.org/modules/docker_compose/#compose-v2
https://velog.io/@byulcode/Docker-compose%EB%A1%9C-Mysql-%EC%84%A4%EC%A0%95
https://jaehoney.tistory.com/222
↓↓↓ 아래 글이 많은 도움이 되었다. Shout out to dkswnkk..
https://dkswnkk.tistory.com/719
'Tech > Spring' 카테고리의 다른 글
SpringBoot 커스텀 프로퍼티에 대한 문서와 자동완성 ※ Spring Configuration Processor (0) | 2024.04.17 |
---|---|
2개 이상의 DB를 사용하는 Spring Boot 환경에서 Spring Data Jpa 사용시 트랜잭션 관련 에러 (0) | 2023.01.29 |
요청으로 들어온 language 값에 따른 GlobalExceptioner에서 다국어 처리 (i18n, yml) + spring validaiton 다국어처리 (0) | 2022.05.23 |
Validation 클래스 단위 제약과 조건부 검사 설정 (0) | 2021.12.22 |
Mybatis와 Jpa 사용시 트랜잭션 묶어서 사용하는 방법(※ 멀티 datasource 설정 / QueryDsl) (0) | 2021.07.24 |