Testcontainers μ‚½μ§ˆ1 : ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆ μž¬μ‚¬μš©

2025. 1. 13. 00:55Β·πŸ“‚ backend/test
 

GitHub - pbg0205/concert

Contribute to pbg0205/concert development by creating an account on GitHub.

github.com

 

 

 ν˜„μž¬ μ‚¬μ΄λ“œ ν”„λ‘œμ νŠΈμ—μ„œ 둜컬 ν™˜κ²½μ—μ„œ main κ³Ό test 의 DB ν™˜κ²½μ„ λ…λ¦½μ μœΌλ‘œ κ΅¬μ„±ν•˜κΈ° μœ„ν•΄ test container λ₯Ό μ μš©ν–ˆλ‹€. Test Container λ₯Ό μ μš©ν•˜λ©° κ²½ν—˜ν–ˆλ˜ μ‚½μ§ˆ 기둝을 κΈ°λ‘ν•΄λ³΄κ³ μž ν•œλ‹€.

 

[1] test container λ₯Ό μ‚¬μš©ν•˜λŠ” 이유

https://docs.localstack.cloud/user-guide/integrations/testcontainers/

  1. μ‹€μ œ μ„œλΉ„μŠ€μ™€ λ™μΌν•œ ν™˜κ²½ : Docker λ₯Ό μ‚¬μš©ν•΄ DB, MQ, API λ“± μ™ΈλΆ€ μ‹œμŠ€ν…œμ„ μ‹€μ œ 운영 ν™˜κ²½μ™€ μœ μ‚¬ν•œ ν™˜κ²½μ—μ„œ ν…ŒμŠ€νŠΈν•  수 μžˆλŠ” μž₯점이 μžˆλ‹€.
  2. ν…ŒμŠ€νŠΈ 독립성 보μž₯ : 각 ν…ŒμŠ€νŠΈλ₯Ό λ…λ¦½λœ μ»¨ν…Œμ΄λ„ˆμ—μ„œ μ‹€ν–‰λ˜λ―€λ‘œ ν…ŒμŠ€νŠΈ 고립성을 보μž₯ν•  수 μžˆλ‹€.

 

[2] ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆ ꡬ성 방법 (with.gradle)

(1) build.gradle

ext {
    set('testcontainers.version', "1.19.8")
}


dependencies {
    testImplementation("org.springframework.boot:spring-boot-testcontainers")
    testImplementation("org.testcontainers:junit-jupiter")
    testImplementation("org.testcontainers:mysql")
}

 

(2) ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆ 생성 방법

ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” @TestContainers, @Container κ°€ ν•„μš”ν•˜λ‹€.

  • @TestContainers
    • ν•΄λ‹Ή ν…ŒμŠ€νŠΈ ν΄λž˜μŠ€κ°€ TestContainers λ₯Ό μ‚¬μš©ν•  것을 μ„ μ–Έν•˜λŠ” 클래슀 레벨 μ–΄λ…Έν…Œμ΄μ…˜(ElementType.TYPE)이닀.
    • ν•΄λ‹Ή μ–΄λ…Έν…Œμ΄μ…˜μ΄ 뢄은 ν΄λž˜μŠ€μ—μ„œ μ„ μ–Έλœ @Container ν•„λ“œμ˜ 생λͺ… μ£ΌκΈ°λ₯Ό κ΄€λ¦¬ν•˜λ©°, ν…ŒμŠ€νŠΈ μ‹œμž‘ μ‹œ μ»¨λ„€μ΄λ„ˆλ₯Ό μžλ™μœΌλ‘œ μ‹œμž‘ν•˜κ³ , ν…ŒμŠ€νŠΈ μ’…λ£Œ μ‹œ 정리λ₯Ό μˆ˜ν–‰ν•œλ‹€.
    • TestContainer λ₯Ό μ‚¬μš©ν•˜λŠ” λͺ¨λ“  ν…ŒμŠ€νŠΈ ν΄λž˜μŠ€μ— λ°˜λ“œμ‹œ μ„ μ–Έν•΄μ•Ό ν•œλ‹€.
  • @Container
    • ν…ŒμŠ€νŠΈμ—μ„œ μ‚¬μš©ν•  μ»¨ν…Œμ΄λ„ˆλ₯Ό μ •μ˜ν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜μ΄λ‹€.
    • ν•΄λ‹Ή ν•„λ“œκ°€ TestcContainers κ°€ κ΄€λ¦¬ν•˜λŠ” μ»¨ν…Œμ΄λ„ˆμž„μ„ λ‚˜νƒ€λ‚΄λ©° 멀버 λ³€μˆ˜ 레벨(ElementType.FIELD) 이닀.
    • @Container 둜 기본적으둜 μ„ μ–Έλœ μ»¨ν…Œμ΄λ„ˆν‹‘ ν…ŒμŠ€νŠΈ μ‹œμž‘ μ‹œ μžλ™ 생성, μ’…λ£Œ μ‹œ μžλ™ μ •λ¦¬λœλ‹€.
    • μ»¨ν…Œμ΄λ„ˆλ₯Ό static 으둜 μ„ μ–Έν•˜λ©΄, ν…ŒμŠ€νŠΈ 클래슀의 λͺ¨λ“  λ©”μ„œλ“œκ°€ 같은 μ»¨ν…Œμ΄λ„ˆλ₯Ό κ³΅μœ ν•œλ‹€.
@Testcontainers
public abstract class RestDocsDocumentationTest {

	@Container
	static MySQLContainer<?> MYSQL_CONTAINER = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
		.withDatabaseName("concert_db")
		.withUsername("root")
		.withPassword("concert2025");
        
	@BeforeAll
	static void beforeAll() {
		MYSQL_CONTAINER.start();
	}

	@AfterAll
	static void afterAll() {
		MYSQL_CONTAINER.stop();
	}
}

 

(3) application.yml μ„€μ •

ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆ μ „μš© spring.datasource.url κ³Ό spring.datasource.driver-class-name 을 μ„€μ •ν•΄μ•Ό ν•œλ‹€.

  • spring.datasource.url : jdbc:tc:[image:version]://[db-name]
  • spring.datasource.driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver
spring:
  datasource:
    url: jdbc:tc:mysql:8://concert_db
    username: root
    password: concert2025
    driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver

 

[3] test container μ‚½μ§ˆ 기둝

(1) ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆ μž¬μ‚¬μš©ν•  λ•Œ @Configuration 을 κ³ λ €ν•΄λ³΄μž. 

class λ‹¨μœ„μ—μ„œ @TestContainers λ₯Ό μ‚¬μš©ν•˜λ©΄ 클래슀 λ‹¨μœ„μ˜ μ»¨ν…Œμ΄λ„ˆ 라이프 사이클 κ΄€λ¦¬λ§Œ κ°€λŠ₯ν•˜λ‹€. 즉, ν…ŒμŠ€νŠΈ 클래슀 μ‹€ν–‰, μ’…λ£Œ λ‹¨μœ„μ—μ„œλ§Œ 라이프 사이클이 κ°€λŠ₯ν•˜λ‹€.

클래슀 λ‹Ή ν•˜λ‚˜μ˜ μ»¨ν…Œμ΄λ„ˆλ₯Ό 생성, μ‹€ν–‰ν•œλ‹€.

 

ν•˜μ§€λ§Œ, @Configuration 을 ν†΅ν•œ @PreDestroy λ₯Ό μ‚¬μš©ν•˜λ©΄ ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ’…λ£Œν•˜λŠ” μ‹œμ μ— μ»¨ν…Œμ΄λ„ˆλ₯Ό μ’…λ£Œν•΄ μ‹€ν–‰, μ’…λ£Œλ₯Ό μ΅œμ†Œν™”ν•˜μ—¬ ν…ŒμŠ€νŠΈ μ‹€ν–‰ μ‹œκ°„μ„ 쀄일 수 μžˆλ‹€.

 

λ˜ν•œ, @Configuration 을 μ„ μ–Έν•˜λ©΄ 톡합 ν…ŒμŠ€νŠΈλ₯Ό λ™μž‘μ‹œν‚€κΈ° μœ„ν•œ μ„€μ •μœΌλ‘œ λ“±λ‘λ˜κ³  static ν‚€μ›Œλ“œλ₯Ό 톡해 TestContainer λ₯Ό μž¬μ‚¬μš©ν•˜κ³  μžˆμ–΄ TestContext Caching으둜 μΈν•œ λ‘œλ”© μ†λ„μ˜ μž₯점이 μžˆμ„ κ²ƒμœΌλ‘œ μ˜ˆμƒν•œλ‹€. (좔가적인 κ²€ν† κ°€ ν•„μš”)

@Testcontainers
@Configuration
public class TestContainerConfiguration {

	@Container
	static MySQLContainer<?> MYSQL_CONTAINER = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));

	static {
		MYSQL_CONTAINER.start();
	}

	@DynamicPropertySource
	static void configureProperties(DynamicPropertyRegistry registry) {
		registry.add("spring.datasource.url", MYSQL_CONTAINER::getJdbcUrl);
		registry.add("spring.datasource.username", MYSQL_CONTAINER::getUsername);
		registry.add("spring.datasource.password", MYSQL_CONTAINER::getPassword);
	}

	@PreDestroy
	void preDestroy() {
		if (MYSQL_CONTAINER.isRunning()) {
			MYSQL_CONTAINER.stop();
		}
	}
}

 

(2) ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆλ₯Ό μ‚¬μš©ν•  λ•Œ ddl-auto option=create λ₯Ό μ‚¬μš©ν•˜κΈ°

ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆλ₯Ό λ„μž…ν•˜κ³  ν…ŒμŠ€νŠΈκ°€ μ™„λ£Œλœ 후에도 ν…ŒμŠ€νŠΈλ₯Ό μ’…λ£Œν•˜μ§€ μ•ŠλŠ” μ΄μŠˆκ°€ μžˆμ—ˆλ‹€. κ²°κ΅­ ν…ŒμŠ€νŠΈκ°€ μ„±κ³΅ν•˜μ—¬ μ’…λ£ŒλŠ” λ˜μ—ˆμ§€λ§Œ μ’…λ£Œν•˜κΈ° μœ„ν•œ μ‹œκ°„ μ§€μ—°μœΌλ‘œ μΈν•œ λΉŒλ“œ μ‹œκ°„μ΄ κΈΈμ–΄μ‘Œλ‹€.

 

원인 확인을 μœ„ν•΄ 둜그λ₯Ό ν™•μΈν•΄λ³΄λ‹ˆ drop table ν•˜λŠ” 과정을 μ§„ν–‰ν•˜λ‹€ connection 이 λŠμ–΄μ§€λŠ” λ¬Έμ œμ˜€λ‹€. drop table 을 ν•˜λŠ” κ³Όμ •μ—μ„œ ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆκ°€ λ¨Όμ € μ’…λ£Œλ˜μ–΄ drop table 을 μž¬μ‹œλ„ν•˜κΈ° μœ„ν•΄ connection 반볡 μ‹œλ„ν•˜λ‹€ κ²°κ΅­ μ‹€νŒ¨ν–ˆλ‹€.

SQL STATE(08S01) : connection 이 λŠμ–΄μ‘Œμ„ λ•Œ λ°œμƒν•˜λŠ” μ—λŸ¬

 

 

drop table 이 μ‹€νŒ¨ν•˜λ©΄μ„œ Test Container μžμ›λ„ νšŒμˆ˜ν•˜μ§€ λͺ»ν•΄ μ»¨ν…Œμ΄λ„ˆκ°€ μ‚­μ œλ˜μ§€ λͺ»ν•œ 것을 확인할 수 μžˆλ‹€.

TestContainer connection 이 도쀑에 ν•΄μ œλ˜λŠ” 문제둜 인해 TestContainer λ₯Ό λ°˜ν™˜ν•˜μ§€ μ•ŠλŠ” 문제

 

 

ν˜„μž¬ μžλ°” μ½”λ“œλ₯Ό ν†΅ν•΄μ„œ TestContainer lifecycle 을 μ œμ–΄ν•˜λŠ”λ° ν•œκ³„κ°€ μžˆμ–΄ κΈ°μ‘΄ spring.jpa.hibernate.ddl-auto μ˜΅μ…˜μ„ create-drop μ—μ„œ create 둜 λ³€κ²½ν–ˆλ‹€. create-drop 은 ν…ŒμŠ€νŠΈκ°€ μ’…λ£Œλ˜λ©΄ drop table 을 ν˜ΈμΆœν•˜λŠ” 반면, create λŠ” ν…ŒμŠ€νŠΈκ°€ μ‹œμž‘λ˜λŠ” μ‹œμ μ— drop table 을 ν˜ΈμΆœν•˜μ—¬ TestContainer 의 lifecycle 을 μ œμ–΄ν•˜μ§€ μ•Šκ³  ν•΄κ²°ν•  수 μžˆλŠ” λ°©λ²•μ΄μ—ˆλ‹€.

ν…ŒμŠ€νŠΈ μ™„λ£Œ ν›„ ν…ŒμŠ€νŠΈ κ΄€λ ¨ μžμ› λ°˜ν™˜ 확인

 

ν…ŒμŠ€νŠΈ μ™„λ£Œ ν›„ test container μžμ› 회수 μ™„λ£Œ 확인

 

 

(4) 이후에 Gradle BuildService 도 ν•œλ²ˆ κ²€ν† ν•΄λ³΄μž. 

이전에도 μ–ΈκΈ‰ν–ˆλ‹€μ‹œν”Ό java code level μ—μ„œ ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆμ˜ 라이프 사이클을 μ‘°μ ˆν•˜λŠ”λ° ν•œκ³„κ°€ μžˆλ‹€. 이λ₯Ό κ°œμ„ ν•  수 μžˆλŠ” 방법 쀑 ν•˜λ‚˜κ°€ Gradle BuildService 라고 ν•œλ‹€.

 

Gradle BuildService λŠ” task μ‚¬μ΄μ˜ μƒνƒœλ‚˜ λ¦¬μ†ŒμŠ€λ₯Ό κ³΅μœ ν•˜κ²Œ ν•΄μ£ΌλŠ” μΈν„°νŽ˜μ΄μŠ€μ΄λ‹€. 이λ₯Ό 톡해 Container λ₯Ό λ“±λ‘ν•˜μ—¬ integration test μ—μ„œ μ‚¬μš©ν•  수 있게 λ§Œλ“€μ–΄μ£Όλ©΄ java code level 보닀 λ”μš± 넓은 λ²”μœ„λ‘œ TestContainer lifecycle 을 μ œμ–΄κ°€ κ°€λŠ₯ν•˜λ‹€. ν•΄λ‹Ή λ‚΄μš©μ€ 이후에 λ‹€μ‹œ κ²€ν† ν•΄λ³Ό μ˜ˆμ •μ΄λ‹€.

  • [flex blog] Testcontainers 에 μ˜ν•œ docker container 생성 ν­λ°œμ„ 막아라

 

Reference

  • [Testcontainer]Getting started with Testcontainers in a Java Spring Boot Project : https://testcontainers.com/guides/testing-spring-boot-rest-api-using-testcontainers/
  • [Testcontainer] Testcontainers container lifecycle management using JUnit 5 : https://testcontainers.com/guides/testcontainers-container-lifecycle/

'πŸ“‚ backend > test' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

docker -compose 기반 둜컬 DB λͺ¨λ‹ˆν„°λ§ ꡬ좕(mysqld-exporter + prometheus + grafana)  (0) 2025.01.25
K6 tutorial  (0) 2024.12.04
nGrinder tutorial  (0) 2024.03.19
pinpoint simple summary  (0) 2024.03.08
'πŸ“‚ backend/test' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€
  • docker -compose 기반 둜컬 DB λͺ¨λ‹ˆν„°λ§ ꡬ좕(mysqld-exporter + prometheus + grafana)
  • K6 tutorial
  • nGrinder tutorial
  • pinpoint simple summary
cooper25
cooper25
  • cooper25
    dev cooper
    cooper25
  • 전체
    였늘
    μ–΄μ œ
    • λΆ„λ₯˜ 전체보기 (84)
      • πŸ“‚ backend (34)
        • spring (19)
        • architecture (10)
        • test (5)
      • πŸ“‚ computer-science (5)
      • πŸ“‚ programming-language (12)
        • java (12)
      • πŸ“‚ infra (9)
        • mysql (4)
        • redis (2)
        • message-queue (3)
      • πŸ“‚ cloud (2)
        • aws (2)
      • πŸ“‚ frontend (1)
        • react (0)
      • πŸ“‚ education & lecture (16)
        • 인프라 곡방 (11)
        • ν•­ν”Œ λ°±μ—”λ“œ 7κΈ° (5)
      • πŸ“‚ ai (1)
        • claude (1)
      • πŸ“‚ etc (2)
        • 회고 (1)
        • 컨퍼런슀 (1)
  • λΈ”λ‘œκ·Έ 메뉴

    • ν™ˆ
    • νƒœκ·Έ
    • λ°©λͺ…둝
  • 링크

  • 곡지사항

  • 인기 κΈ€

  • νƒœκ·Έ

    λ™μ‹œμ„±
    인프라곡방
    mysql
    데이터 쀑심 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 섀계
    가상 λ©΄μ ‘ μ‚¬λ‘€λ‘œ λ°°μš°λŠ” λŒ€κ·œλͺ¨ 섀계
    nGrinder
    spring
    μŠ€ν”„λ§μΊ ν”„ ν›„κΈ°
    Redisson
    spring camp 2025
    AWS
    gof
    spring-batch
    회고
    JPA
    react
    spring AOP
    kafka
    UUID
    ν•­ν•΄ν”ŒλŸ¬μŠ€
  • 졜근 λŒ“κΈ€

  • 졜근 κΈ€

  • hELLOΒ· Designed Byμ •μƒμš°.v4.10.6
cooper25
Testcontainers μ‚½μ§ˆ1 : ν…ŒμŠ€νŠΈ μ»¨ν…Œμ΄λ„ˆ μž¬μ‚¬μš©
μƒλ‹¨μœΌλ‘œ

ν‹°μŠ€ν† λ¦¬νˆ΄λ°”