Post

TDD-02

TDD-02

🎭 ν…ŒμŠ€νŠΈ 더블 (Test Double): Mockκ³Ό Stub

ν…ŒμŠ€νŠΈ 더블(Test Double)은 μ‹€μ œ 객체(Real Object)λ₯Ό λŒ€μ‹ ν•˜μ—¬ ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•΄ μ‚¬μš©λ˜λŠ” λͺ¨λ“  μ’…λ₯˜μ˜ λŒ€μ—­ 객체λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€. μ™ΈλΆ€ μ‹œμŠ€ν…œ(DB, API λ“±)에 λŒ€ν•œ μ˜μ‘΄μ„±μ„ μ œκ±°ν•˜μ—¬ 독립적이고, λΉ λ₯΄λ©°, μ‹ λ’°μ„± 높은 ν…ŒμŠ€νŠΈ ν™˜κ²½μ„ κ΅¬μΆ•ν•˜λŠ” 데 핡심적인 역할을 ν•©λ‹ˆλ‹€.

그쀑 κ°€μž₯ λŒ€ν‘œμ μΈ 것이 Mockκ³Ό Stubμž…λ‹ˆλ‹€.

  • Mock (ν–‰μœ„ 검증)

    νŠΉμ • ν•¨μˆ˜κ°€ ν˜ΈμΆœλ˜μ—ˆλŠ”μ§€, μ •ν•΄μ§„ 행동을 μˆ˜ν–‰ν–ˆλŠ”μ§€ κ²€μ¦ν•˜λŠ” 데 μ‚¬μš©λ˜λ©°, 객체의 행동(Behavior)에 μ΄ˆμ μ„ 맞μΆ₯λ‹ˆλ‹€.

    μ˜ˆμ‹œ: verify(mockRepository, times(1)).save(any(User.class)); 와 같이 save λ©”μ„œλ“œκ°€ 1번 ν˜ΈμΆœλ˜μ—ˆλŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€.

  • Stub (μƒνƒœ μ œμ–΄)

    미리 μ€€λΉ„λœ 응닡을 μ œκ³΅ν•˜μ—¬ ν…ŒμŠ€νŠΈκ°€ μ›ν•˜λŠ” νŠΉμ • μƒνƒœλ₯Ό 갖도둝 λ§Œλ“œλŠ” 데 μ‚¬μš©λ˜λ©°, 객체의 μƒνƒœ(State)에 μ΄ˆμ μ„ 맞μΆ₯λ‹ˆλ‹€.

    μ˜ˆμ‹œ: when(stubRepository.findById(1L)).thenReturn(Optional.of(user)); 와 같이 findById(1L)이 호좜되면 미리 μ€€λΉ„λœ user 객체λ₯Ό λ°˜ν™˜ν•˜λ„λ‘ μ„€μ •ν•©λ‹ˆλ‹€.

Mock은 Stub의 κΈ°λŠ₯을 포함할 수 μžˆμŠ΅λ‹ˆλ‹€. λ‘˜μ„ κ΅¬λΆ„ν•˜λŠ” 핡심 기쀀은 β€˜κ²€μ¦μ˜ λͺ©μ β€™μž…λ‹ˆλ‹€.

κ²€μ¦ν•˜λ €λŠ” λͺ©μ μ΄ β€˜νŠΉμ • λ©”μ„œλ“œμ˜ 호좜 여뢀’라면 Mock을, β€˜ν…ŒμŠ€νŠΈ 싀행을 μœ„ν•œ νŠΉμ • μƒνƒœλ‚˜ λ°˜ν™˜ 값’이 μ€‘μš”ν•˜λ‹€λ©΄ Stub을 μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.


πŸ”Ί ν…ŒμŠ€νŠΈ ν”ΌλΌλ―Έλ“œ (Test Pyramid) 및 ν…ŒμŠ€νŠΈ μ’…λ₯˜

ν…ŒμŠ€νŠΈ ν”ΌλΌλ―Έλ“œλŠ” 효과적인 ν…ŒμŠ€νŠΈ μ „λž΅μ„ μ‹œκ°μ μœΌλ‘œ λ‚˜νƒ€λ‚΄λŠ” λͺ¨λΈμž…λ‹ˆλ‹€. ν”ΌλΌλ―Έλ“œμ˜ 각 측은 ν…ŒμŠ€νŠΈμ˜ μ’…λ₯˜, λ²”μœ„, λΉ„μš©, μ‹€ν–‰ 속도 κ°„μ˜ 관계λ₯Ό λ³΄μ—¬μ€λ‹ˆλ‹€.

κ°€μž₯ 효율적인 ν…ŒμŠ€νŠΈ μ „λž΅μ€ ν”ΌλΌλ―Έλ“œμ˜ μ•„λž˜μͺ½(λ‹¨μœ„ ν…ŒμŠ€νŠΈ)에 μ§‘μ€‘ν•˜κ³ , 상단뢀(E2E ν…ŒμŠ€νŠΈ)λŠ” μ΅œμ†Œν™”ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.


πŸ§ͺ Unit Testing (λ‹¨μœ„ ν…ŒμŠ€νŠΈ)

  • 🎯 λŒ€μƒ: 단일 κΈ°λŠ₯, ν•¨μˆ˜, ν΄λž˜μŠ€μ™€ 같은 κ°€μž₯ μž‘μ€ μ½”λ“œ 블둝.
  • πŸ” λͺ©μ : μ½”λ“œ 쑰각이 λ…λ¦½μ μœΌλ‘œ μ •ν™•ν•˜κ²Œ λ™μž‘ν•˜λŠ”μ§€ λΉ λ₯΄κ²Œ κ²€μ¦ν•©λ‹ˆλ‹€.
  • ✨ νŠΉμ§•: μ™ΈλΆ€ μ‹œμŠ€ν…œ 의쑴 없이 λ©”λͺ¨λ¦¬ μƒμ—μ„œ 맀우 λΉ λ₯΄κ²Œ μ‹€ν–‰λ©λ‹ˆλ‹€. μ½”λ“œ λ³€κ²½ μ‹œ κ°€μž₯ λ¨Όμ €, 그리고 κ°€μž₯ 자주 μ‹€ν–‰λ˜μ–΄ 즉각적인 ν”Όλ“œλ°±μ„ μ œκ³΅ν•©λ‹ˆλ‹€.
  • ✍️ μž‘μ„± μ‹œμ : λͺ¨λ“  κ°œλ°œμžκ°€ μ½”λ“œ μž‘μ„±κ³Ό λ™μ‹œμ— κ°€μž₯ λΉˆλ²ˆν•˜κ²Œ μž‘μ„±ν•˜λŠ” κΈ°λ³Έ ν…ŒμŠ€νŠΈμž…λ‹ˆλ‹€.

β€œκ°•μ˜μ—μ„œλŠ” λ‹€λ₯Έ κ°μ²΄λ‚˜ μ‹œμŠ€ν…œμ— μ˜μ‘΄ν•˜μ§€ μ•Šκ³ , μ˜¨μ „νžˆ μžμ‹ μ˜ κΈ°λŠ₯만 ν…ŒμŠ€νŠΈν•˜λŠ” 독립적인 ν…ŒμŠ€νŠΈλΌκ³  μ •μ˜ν•©λ‹ˆλ‹€.”

🧩 Integration Testing (톡합 ν…ŒμŠ€νŠΈ)

  • 🎯 λŒ€μƒ: μ„œλ‘œ λ‹€λ₯Έ λͺ¨λ“ˆ, μ»΄ν¬λ„ŒνŠΈ, μ„œλΉ„μŠ€ κ°„μ˜ μƒν˜Έμž‘μš©.
  • πŸ” λͺ©μ : μ—¬λŸ¬ λͺ¨λ“ˆμ΄ ν†΅ν•©λ˜μ—ˆμ„ λ•Œ μ˜ˆμƒλŒ€λ‘œ ν˜‘λ ₯ν•˜κ³  λ™μž‘ν•˜λŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€.
  • ✨ νŠΉμ§•: λ‹¨μœ„ ν…ŒμŠ€νŠΈλ³΄λ‹€ 넓은 λ²”μœ„λ₯Ό 닀루며, Mock을 μ‚¬μš©ν•˜κ±°λ‚˜ μ‹€μ œ μ‹œμŠ€ν…œ(e.g., In-memory DB)을 ν™œμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • ✍️ μž‘μ„± μ‹œμ : 핡심 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ΄λ‚˜ μ„œλΉ„μŠ€ κ°„μ˜ μ€‘μš”ν•œ μƒν˜Έμž‘μš© 지점을 검증할 λ•Œ μž‘μ„±ν•©λ‹ˆλ‹€.

μ‹€λ¬΄μ—μ„œλŠ” λͺ©μ μ— 따라 두 μ’…λ₯˜λ‘œ λ‚˜λˆŒ 수 μžˆμŠ΅λ‹ˆλ‹€.

  1. 개발 레벨 톡합 ν…ŒμŠ€νŠΈ: μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λ‚΄λΆ€μ—μ„œ μ—¬λŸ¬ μ»΄ν¬λ„ŒνŠΈ(Controller-Service-Repository)κ°€ μ •μƒμ μœΌλ‘œ μ—°λ™λ˜λŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€. (JUnit + @SpringBootTest, Testcontainers, H2 등을 μ‚¬μš©)

    λ‹¨μœ„ ν…ŒμŠ€νŠΈμ™€ 달리 β€œμ‹€μ œ Bean κ°„ 연동, DB νŠΈλžœμž­μ…˜ λ“± μ‹€μ œ ν™˜κ²½μ— κ°€κΉŒμš΄ 상황을 κ²€μ¦β€ν•˜λŠ” 데 λͺ©μ μ΄ μžˆμŠ΅λ‹ˆλ‹€.

  2. QA/배포 단계 톡합 ν…ŒμŠ€νŠΈ (SIT): μ—¬λŸ¬ μ„œλΉ„μŠ€κ°€ ν†΅ν•©λœ ν™˜κ²½(e.g., Staging)μ—μ„œ 전체 μ‹œμŠ€ν…œμ˜ 연동을 ν™•μΈν•©λ‹ˆλ‹€. 주둜 QAνŒ€μ΄ μ‹œλ‚˜λ¦¬μ˜€ 기반으둜 ν…ŒμŠ€νŠΈλ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€.

πŸ–₯️ End-to-End Testing (E2E ν…ŒμŠ€νŠΈ)

  • 🎯 λŒ€μƒ: μ‹€μ œ μ‚¬μš©μžμ˜ μ‹œλ‚˜λ¦¬μ˜€μ™€ λ™μΌν•œ 전체 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 흐름.
  • πŸ” λͺ©μ : μ΅œμ’… μ‚¬μš©μž κ΄€μ μ—μ„œ μ‹œμŠ€ν…œ 전체가 μ²˜μŒλΆ€ν„° λκΉŒμ§€ 정상 μž‘λ™ν•˜λŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€.
  • ✨ νŠΉμ§•: μ‹€μ œμ™€ λ™μΌν•œ ν™˜κ²½μ—μ„œ μˆ˜ν–‰λ˜λ―€λ‘œ 신뒰도가 λ†’μ§€λ§Œ, μž‘μ„± λΉ„μš©μ΄ 크고 μ‹€ν–‰ μ‹œκ°„μ΄ 맀우 κΉλ‹ˆλ‹€. λ”°λΌμ„œ 핡심 μ‹œλ‚˜λ¦¬μ˜€μ— λŒ€ν•΄μ„œλ§Œ μ΅œμ†Œν•œμœΌλ‘œ μž‘μ„±ν•΄μ•Ό ν•©λ‹ˆλ‹€.
  • ✍️ μž‘μ„± μ‹œμ : 배포 직전, μ‹œμŠ€ν…œμ˜ μ΅œμ’…μ μΈ μ•ˆμ •μ„±μ„ 보μž₯ν•˜κΈ° μœ„ν•΄ μ‹€ν–‰ν•©λ‹ˆλ‹€. (Cypress, Playwright, Selenium, RestAssured λ“±μ˜ 도ꡬλ₯Ό μ‚¬μš©)

πŸ“ Testable Code μž‘μ„±λ²•κ³Ό μ£Όμ˜μ‚¬ν•­

  • λͺ¨λ“  ν”„λ‘œλ•μ…˜ μ½”λ“œλŠ” ν…ŒμŠ€νŠΈ κ°€λŠ₯ν•˜κ²Œ(Testable) κ΅¬ν˜„ν•˜λŠ” 것을 λͺ©ν‘œλ‘œ μ‚ΌμŠ΅λ‹ˆλ‹€.
  • ν…ŒμŠ€νŠΈ 컀버리지 100%에 μ§‘μ°©ν•˜κΈ°λ³΄λ‹€, 의미 있고 μ‹ λ’°μ„± 높은 ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜λŠ” 데 μ§‘μ€‘ν•©λ‹ˆλ‹€.
  • ν…ŒμŠ€νŠΈ μž‘μ„±μ„ μ–΄λ ΅κ²Œ λ§Œλ“œλŠ” μ½”λ“œ(e.g., private λ©”μ„œλ“œ, κ°•ν•œ κ²°ν•©)λŠ” λ¦¬νŒ©ν† λ§μ„ 톡해 κ°œμ„ ν•©λ‹ˆλ‹€.
  • ν…ŒμŠ€νŠΈμ˜ 성곡이 κ³§ κΈ°λŠ₯ κ΅¬ν˜„μ˜ μ™„λ£Œλ₯Ό μ˜λ―Έν•˜λ„λ‘ 개발 ν”„λ‘œμ„ΈμŠ€λ₯Ό μ •λ¦½ν•©λ‹ˆλ‹€.

πŸ› οΈ 싀무 ν…ŒμŠ€νŠΈ μ½”λ“œ μž‘μ„± μ‹œ 고렀사항 및 κ·œμΉ™

μ‹€λ¬΄μ—μ„œλŠ” λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό 기본으둜 μ‚ΌλŠ” 것이 κ°€μž₯ μ•ˆμ •μ μ΄κ³  νš¨μœ¨μ μž…λ‹ˆλ‹€. λ‹¨μœ„ ν…ŒμŠ€νŠΈλŠ” μ™ΈλΆ€ μš”μΈμœΌλ‘œλΆ€ν„° 개발자λ₯Ό λ³΄ν˜Έν•˜λŠ” κ°€μž₯ κ°•λ ₯ν•œ λ°©μ–΄ μˆ˜λ‹¨μž…λ‹ˆλ‹€.

βœ… μ§€μΌœμ•Ό ν•  ν…ŒμŠ€νŠΈ μ½”λ“œ κ·œμΉ™

  • ν•œ ν…ŒμŠ€νŠΈλŠ” ν•œ κ°€μ§€ μ±…μž„λ§Œ 검증: 각 ν…ŒμŠ€νŠΈλŠ” λͺ…ν™•ν•œ 단일 λͺ©μ μ„ κ°€μ Έμ•Ό ν•©λ‹ˆλ‹€.
  • μ™ΈλΆ€ μ˜μ‘΄μ„± μ œμ–΄: μ‹€μ œ DBλ‚˜ μ™ΈλΆ€ API λŒ€μ‹  Mock 객체λ₯Ό μ‚¬μš©ν•˜μ—¬ ν…ŒμŠ€νŠΈμ˜ 독립성과 속도λ₯Ό ν™•λ³΄ν•©λ‹ˆλ‹€.
  • ν…ŒμŠ€νŠΈ μž¬ν˜„μ„± 확보: μ‹€ν–‰ν•  λ•Œλ§ˆλ‹€ κ²°κ³Όκ°€ λ‹¬λΌμ§€λŠ” 랜덀 κ°’μ΄λ‚˜ ν˜„μž¬ μ‹œκ°„(e.g., LocalDateTime.now())에 μ˜μ‘΄ν•˜λŠ” μ½”λ“œλ₯Ό μ œκ±°ν•˜μ—¬ μΌκ΄€λœ κ²°κ³Όλ₯Ό 보μž₯ν•©λ‹ˆλ‹€.
  • λͺ…ν™•ν•œ ν…ŒμŠ€νŠΈ 이름 (Given-When-Then): ν…ŒμŠ€νŠΈμ˜ 쑰건(Given), μ‹€ν–‰(When), κΈ°λŒ€ κ²°κ³Ό(Then)κ°€ λ“œλŸ¬λ‚˜λ„λ‘ 이름을 μ§€μ–΄, μ΄λ¦„λ§Œ 봐도 ν…ŒμŠ€νŠΈμ˜ μ˜λ„λ₯Ό νŒŒμ•…ν•  수 있게 ν•©λ‹ˆλ‹€.
  • λͺ…ν™•ν•œ Assertion: isTrue(), isNotNull()κ³Ό 같은 λͺ¨ν˜Έν•œ 검증 λŒ€μ‹ , κΈ°λŒ€ν•˜λŠ” κ°’μ΄λ‚˜ μƒνƒœλ₯Ό λͺ…ν™•νžˆ λͺ…μ‹œν•©λ‹ˆλ‹€. (e.g., isEqualTo(expectedValue))

❌ ν”Όν•΄μ•Ό ν•  μ•ˆν‹°νŒ¨ν„΄

  • ν…ŒμŠ€νŠΈ κ°„ μƒνƒœ 곡유: ν…ŒμŠ€νŠΈλŠ” μ„œλ‘œ 독립적이어야 ν•©λ‹ˆλ‹€. @BeforeEach 등을 ν™œμš©ν•˜μ—¬ 맀번 κΉ¨λ—ν•œ μƒνƒœμ—μ„œ μ‹œμž‘ν•˜λ„λ‘ ν•©λ‹ˆλ‹€.
  • Thread.sleep() μ‚¬μš©: ν…ŒμŠ€νŠΈλ₯Ό 느리고 λΆˆμ•ˆμ •ν•˜κ²Œ λ§Œλ“­λ‹ˆλ‹€. 비동기 μ½”λ“œλ₯Ό ν…ŒμŠ€νŠΈν•  λ•ŒλŠ” Awaitility와 같은 μ „λ¬Έ 라이브러리λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.
  • 의미 μ—†λŠ” ν…ŒμŠ€νŠΈ 이름: test1(), doTest()와 같이 λͺ©μ μ„ μ•Œ 수 μ—†λŠ” 이름은 μ§€μ–‘ν•©λ‹ˆλ‹€.
  • κ³Όλ„ν•œ Mock 검증: λ‚΄λΆ€ κ΅¬ν˜„μ— λ„ˆλ¬΄ 깊이 κ΄€μ—¬ν•˜λŠ” ν…ŒμŠ€νŠΈλŠ” λ¦¬νŒ©ν† λ§μ„ λ°©ν•΄ν•©λ‹ˆλ‹€.

β€œμ΄ λ©”μ„œλ“œκ°€ λͺ‡ 번 ν˜ΈμΆœλ˜μ—ˆλ‚˜β€μ™€ 같은 λ‚΄λΆ€ κ΅¬ν˜„ 방식에 μ§‘μ°©ν•˜κΈ°λ³΄λ‹€, β€œκ·Έλž˜μ„œ μ΅œμ’…μ μœΌλ‘œ μ™ΈλΆ€λ‘œ λ³΄μ΄λŠ” κ²°κ³Όκ°€ 무엇인가” λΌλŠ” ν–‰μœ„(Behavior) μ€‘μ‹¬μ˜ 검증에 μ§‘μ€‘ν•˜λŠ” 것이 더 κ²¬κ³ ν•œ ν…ŒμŠ€νŠΈλ₯Ό λ§Œλ“­λ‹ˆλ‹€.

This post is licensed under CC BY 4.0 by the author.