fix: DB 테스트 하네스에 Testcontainers 싱글턴 컨테이너 패턴 적용
@Testcontainers/@Container를 쓰면 클래스마다 컨테이너가 stop되는데 Spring 컨텍스트 캐시는 죽은 포트를 가리키는 DataSource를 재사용해, 전체 스위트 실행 시 두 번째로 실행되는 DB 테스트 클래스가 순서에 따라 Connection refused로 깨졌다. 컨테이너를 static 블록에서 한 번만 start하고 절대 stop하지 않도록 바꿔 모든 AbstractDbTest 하위 클래스가 동일한 컨텍스트/ 컨테이너를 공유하게 했다. SchemaMigrationTest는 자체 스캐폴딩을 지우고 AbstractDbTest를 상속하도록 정리했다.
@27f606ddeb6b0b8f2bd3ea16b3880c59eab707bb
--- src/test/java/kr/itn/itnhub/AbstractDbTest.java
+++ src/test/java/kr/itn/itnhub/AbstractDbTest.java
... | ... | @@ -5,8 +5,6 @@ |
| 5 | 5 |
import org.springframework.test.context.DynamicPropertyRegistry; |
| 6 | 6 |
import org.springframework.test.context.DynamicPropertySource; |
| 7 | 7 |
import org.testcontainers.containers.PostgreSQLContainer; |
| 8 |
-import org.testcontainers.junit.jupiter.Container; |
|
| 9 |
-import org.testcontainers.junit.jupiter.Testcontainers; |
|
| 10 | 8 |
|
| 11 | 9 |
/** |
| 12 | 10 |
* Spring 컨텍스트를 실제로 로딩해야 하는 테스트의 공통 베이스. |
... | ... | @@ -14,14 +12,27 @@ |
| 14 | 12 |
* <p>Testcontainers PostgreSQL 컨테이너를 띄우고, 애플리케이션 구동에 필요한 |
| 15 | 13 |
* {@code mattermost.*} / {@code app.admin.*} 필수 프로퍼티를 더미 값으로 채워
|
| 16 | 14 |
* 컨텍스트 리프레시가 실제 외부 시스템 없이도 성공하도록 한다.</p> |
| 15 |
+ * |
|
| 16 |
+ * <p><b>싱글턴 컨테이너 패턴.</b> 컨테이너는 테스트 JVM 전체에서 단 한 번만 |
|
| 17 |
+ * static 초기화 블록에서 직접 {@code start()} 하고, JUnit5의 {@code @Testcontainers}/
|
|
| 18 |
+ * {@code @Container} 확장은 의도적으로 쓰지 않는다. 그 확장을 쓰면 매 테스트 클래스가
|
|
| 19 |
+ * 끝날 때마다 컨테이너가 stop 되는데, Spring의 테스트 컨텍스트 캐시는 동일한 설정을 |
|
| 20 |
+ * 공유하는 다음 {@code AbstractDbTest} 하위 클래스에 대해 이미 만들어진
|
|
| 21 |
+ * {@code ApplicationContext}(및 그 안에서 죽은 포트를 가리키는 {@code DataSource})를
|
|
| 22 |
+ * 그대로 재사용해버려, 두 번째로 실행되는 DB 테스트 클래스가 "Connection refused"로 |
|
| 23 |
+ * 깨진다(전체 스위트에서만 재현되고 단독 실행 시엔 통과하는 이유). 여기서 컨테이너를 |
|
| 24 |
+ * 한 번만 띄우고 절대 {@code stop()} 하지 않으면(JVM 종료 시 Ryuk 사이드카가 정리)
|
|
| 25 |
+ * 모든 하위 테스트 클래스가 동일한 살아있는 컨테이너를 공유하므로 이 문제가 사라진다.</p> |
|
| 17 | 26 |
*/ |
| 18 | 27 |
@SpringBootTest |
| 19 |
-@Testcontainers |
|
| 20 | 28 |
public abstract class AbstractDbTest {
|
| 21 | 29 |
|
| 22 |
- @Container |
|
| 23 | 30 |
@ServiceConnection |
| 24 |
- static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
|
|
| 31 |
+ static final PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
|
|
| 32 |
+ |
|
| 33 |
+ static {
|
|
| 34 |
+ postgres.start(); |
|
| 35 |
+ } |
|
| 25 | 36 |
|
| 26 | 37 |
@DynamicPropertySource |
| 27 | 38 |
static void secrets(DynamicPropertyRegistry registry) {
|
--- src/test/java/kr/itn/itnhub/SchemaMigrationTest.java
+++ src/test/java/kr/itn/itnhub/SchemaMigrationTest.java
... | ... | @@ -2,35 +2,13 @@ |
| 2 | 2 |
|
| 3 | 3 |
import org.junit.jupiter.api.Test; |
| 4 | 4 |
import org.springframework.beans.factory.annotation.Autowired; |
| 5 |
-import org.springframework.boot.test.context.SpringBootTest; |
|
| 6 |
-import org.springframework.boot.testcontainers.service.connection.ServiceConnection; |
|
| 7 | 5 |
import org.springframework.dao.DataIntegrityViolationException; |
| 8 | 6 |
import org.springframework.jdbc.core.JdbcTemplate; |
| 9 |
-import org.springframework.test.context.DynamicPropertyRegistry; |
|
| 10 |
-import org.springframework.test.context.DynamicPropertySource; |
|
| 11 |
-import org.testcontainers.containers.PostgreSQLContainer; |
|
| 12 |
-import org.testcontainers.junit.jupiter.Container; |
|
| 13 |
-import org.testcontainers.junit.jupiter.Testcontainers; |
|
| 14 | 7 |
|
| 15 | 8 |
import static org.assertj.core.api.Assertions.assertThat; |
| 16 | 9 |
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
| 17 | 10 |
|
| 18 |
-@SpringBootTest |
|
| 19 |
-@Testcontainers |
|
| 20 |
-class SchemaMigrationTest {
|
|
| 21 |
- |
|
| 22 |
- @Container |
|
| 23 |
- @ServiceConnection |
|
| 24 |
- static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
|
|
| 25 |
- |
|
| 26 |
- @DynamicPropertySource |
|
| 27 |
- static void secrets(DynamicPropertyRegistry registry) {
|
|
| 28 |
- registry.add("app.admin.username", () -> "admin");
|
|
| 29 |
- registry.add("app.admin.password", () -> "test-password");
|
|
| 30 |
- registry.add("mattermost.base-url", () -> "http://localhost:1");
|
|
| 31 |
- registry.add("mattermost.token", () -> "test-token");
|
|
| 32 |
- registry.add("mattermost.team-id", () -> "test-team");
|
|
| 33 |
- } |
|
| 11 |
+class SchemaMigrationTest extends AbstractDbTest {
|
|
| 34 | 12 |
|
| 35 | 13 |
@Autowired |
| 36 | 14 |
JdbcTemplate jdbc; |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?