Post

πŸ’₯ Testcontainers μ»¨ν…Œμ΄λ„ˆ 반볡 생성 문제

πŸ’₯ Testcontainers μ»¨ν…Œμ΄λ„ˆ 반볡 생성 문제

문제 상황

  • Testcontainersλ₯Ό 톡해 ν…ŒμŠ€νŠΈ μš©λ„μ˜ PostgreSQL μ»¨ν…Œμ΄λ„ˆλ₯Ό λ„μš°κ³ μž 좔상 클래슀λ₯Ό μƒμ„±ν–ˆλ‹€.
  • ν•˜λ‚˜μ˜ μ»¨ν…Œμ΄λ„ˆλ‘œ λͺ¨λ“  μžμ‹ ν΄λž˜μŠ€λ“€μ˜ ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•  것이라 μ˜ˆμƒν–ˆμ§€λ§Œ κ·Έλ ‡μ§€ μ•Šμ•˜λ‹€.
  • μ‹€μ œλ‘œλŠ” μžμ‹ 클래슀 λ³„λ‘œ μ»¨ν…Œμ΄λ„ˆκ°€ μƒˆλ‘œ μƒμ„±λ˜μ—ˆμœΌλ©°, ν•΄λ‹Ή μ»¨ν…Œμ΄λ„ˆμ— μ—°κ²° λ˜ν•œ μ‹€νŒ¨ν–ˆλ‹€.

문제 원인

1
2
3
4
5
6
7
8
9
10
11
12
13
@Testcontainers 
public abstract class ContainerBaseTest { 
	@Container 
	static PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:15-alpine"); 
	
	@DynamicPropertySource 
	static void overrideProps(DynamicPropertyRegistry registry) { 
		registry.add("spring.datasource.url", postgresContainer::getJdbcUrl);
		registry.add("spring.datasource.username", postgresContainer::getUsername);
		registry.add("spring.datasource.password", postgresContainer::getPassword);
		registry.add("file.upload-dir", () -> "/test-uploads"); 
	} 
}
  • @Container Annotation의 λ™μž‘ 방식이 λ¬Έμ œκ°€ λ˜μ—ˆλ‹€.
  • TestcontainersλŠ” @Containerκ°€ 뢙은 ν•„λ“œλ₯Ό λ°œκ²¬ν•˜λ©΄ ν•΄λ‹Ή μ»¨ν…Œμ΄λ„ˆμ˜ 라이프 사이클을 μžλ™μœΌλ‘œ κ΄€λ¦¬ν•œλ‹€.
  • 일반적으둜 static ν•„λ“œμ— @Containerλ₯Ό 뢙이면 ν•΄λ‹Ή ContainerλŠ” ν…ŒμŠ€νŠΈ 클래슀 μ „μ²΄μ—μ„œ ν•œ 번만 μ‹œμž‘λ˜κ³  μ’…λ£Œλœλ‹€.
  • λ¬Έμ œλŠ” 이 static ν•„λ“œκ°€ 좔상 ν΄λž˜μŠ€μ— μ •μ˜λ˜μ–΄ μžˆμ„ λ•Œ λ°œμƒν•œλ‹€.
  • Testcontainers의 JUnit 5 톡합 ν…ŒμŠ€νŠΈλŠ” 좔상 ν΄λž˜μŠ€μ— μ •μ˜λœ static @Container ν•„λ“œλ₯Ό μ²˜λ¦¬ν•  λ•Œ, 이λ₯Ό 상속 λ°›λŠ” 각 ꡬ체적인 ν…ŒμŠ€νŠΈ ν΄λž˜μŠ€λ§ˆλ‹€ λ³„λ„μ˜ Container μΈμŠ€ν„΄μŠ€λ₯Ό μ‹œμž‘ν•˜λ„λ‘ λ™μž‘ν•œλ‹€.
  • μ΄λŠ” Testcontainersκ°€ 각 ν…ŒμŠ€νŠΈ 클래슀 μ»¨ν…μŠ€νŠΈ λ‚΄μ—μ„œ static ν•„λ“œλ₯Ό μž¬μ •μ˜ν•˜λŠ” λ°©μ‹μœΌλ‘œ μ²˜λ¦¬ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

ν•΄κ²° 방법

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
@Testcontainers  
public abstract class ContainerBaseTest {  
	private static final PostgreSQLContainer<?> POSTGRES_CONTAINER;  
	
	@BeforeAll  
	static void init() {  
		Dotenv dotenv = Dotenv.load();  
		dotenv.entries().forEach(entry ->  
			System.setProperty(entry.getKey(), entry.getValue())  
		);  
	}  
	
	static {  
		POSTGRES_CONTAINER = new PostgreSQLContainer<>("postgres:15-alpine");  
		POSTGRES_CONTAINER.start();  
	}  
	
	@DynamicPropertySource  
	static void overrideProps(DynamicPropertyRegistry registry) {  
		registry.add("spring.datasource.url", POSTGRES_CONTAINER::getJdbcUrl);  
		registry.add("spring.datasource.username", POSTGRES_CONTAINER::getUsername);  
		registry.add("spring.datasource.password", POSTGRES_CONTAINER::getPassword);  
		registry.add("file.upload-dir", () -> "/test-uploads");  
	}  
}
  • @Container Annotation을 μ‚¬μš©ν•˜μ§€ μ•Šκ³  static λΈ”λ‘μ—μ„œ Containerλ₯Ό μˆ˜λ™μœΌλ‘œ μ‹œμž‘ν•˜λŠ” 싱글톀 Containerλ₯Ό κ΅¬μ„±ν•˜λŠ” 것이닀.
  • ContainerBaseTest ν΄λž˜μŠ€κ°€ JVM에 λ‘œλ“œλ  λ•Œ static 블둝이 단 ν•œ 번만 μ‹€ν–‰λ˜μ–΄ μ»¨ν…Œμ΄λ„ˆκ°€ ν•œ 번만 μƒμ„±λœλ‹€.
  • 이후 ContainerBaseTestλ₯Ό 상속 λ°›λŠ” λͺ¨λ“  ν…ŒμŠ€νŠΈ ν΄λž˜μŠ€λŠ” 이 이미 μ‹œμž‘λœ 단일 μ»¨ν…Œμ΄λ„ˆ μΈμŠ€ν„΄μŠ€λ₯Ό κ³΅μœ ν•˜κ²Œ λœλ‹€.

회고

  • @Container Annotation을 μ‚¬μš©ν•˜μ—¬ νŠΉμ • μ»¨ν…Œμ΄λ„ˆμ˜ 라이프 사이클을 μžλ™μœΌλ‘œ κ΄€λ¦¬ν•˜κ²Œ ν•œλ‹€λŠ” κ°œλ…μ„ μ•Œλ”λΌλ„ ꡬ체적인 λ‚΄λΆ€ λ™μž‘μ„ μ•Œμ•„μ•Ό μ˜λ„ν•œ λŒ€λ‘œ ν™˜κ²½μ„ ꡬ좕할 수 μžˆλ‹€λŠ” 사싀을 μ²΄κ°ν–ˆλ‹€.
This post is licensed under CC BY 4.0 by the author.