본문으둜 κ±΄λ„ˆλ›°κΈ°

🌈 Chapter 24: ν…ŒμŠ€νŠΈ μœ μ—°μ„±

  • ν…ŒμŠ€νŠΈ λΆˆμ•ˆμ •μ„±μ˜ 곡톡적인 원인은 λ‹€μŒκ³Ό κ°™λ‹€.
    • ν…ŒμŠ€νŠΈκ°€ μ‹œμŠ€ν…œμ—μ„œ 관련이 μ—†λŠ” λΆ€λΆ„μ΄λ‚˜ ν…ŒμŠ€νŠΈ λŒ€μƒ 객체에 λ¬΄κ΄€ν•œ ν–‰μœ„μ™€ λ„ˆλ¬΄ κΈ΄λ°€ν•˜κ²Œ 결햅돼 μžˆλ‹€.
    • ν…ŒμŠ€νŠΈκ°€ λŒ€μƒ μ½”λ“œμ˜ μ˜ˆμƒ ν–‰μœ„λ₯Ό κ³Όλ„ν•˜κ²Œ κΈ°μˆ ν•΄μ„œ ν•„μš” μ΄μƒμœΌλ‘œ μ œμ•½ν•œλ‹€.
    • μ—¬λŸ¬ ν…ŒμŠ€νŠΈμ—μ„œ λ™μΌν•œ μ œν’ˆ μ½”λ“œμ˜ ν–‰μœ„λ₯Ό μ‹œν—˜ν•  λ•Œ 쀑볡이 생긴닀.
  • ν…ŒμŠ€νŠΈ λΆˆμ•ˆμ •μ„±μ€ μ‹œμŠ€ν…œ 섀계와도 관련이 μžˆλŠ”λ° μ–΄λ–€ 객체가 μ˜μ‘΄μ„±μ΄ λ„ˆλ¬΄ λ§Žκ±°λ‚˜ ν•΄λ‹Ή 객체의 μ˜μ‘΄μ„±μ΄ 감좰져 μžˆμ–΄ ν™˜κ²½κ³Ό λΆ„λ¦¬ν•˜κΈ°κ°€ μ–΄λ ΅λ‹€λ©΄ ν•΄λ‹Ή 객체의 ν…ŒμŠ€νŠΈλŠ” 객체와 동떨어진 뢀뢄이 변경될 λ•Œ μ‹€νŒ¨ν•  것이닀.
  • ν…ŒμŠ€νŠΈ 가독성과 회볡λ ₯은 λ—„ 수 μ—†λŠ” 관계며, λ¬Έμ œμ— μ§‘μ€‘ν•˜κ³ , λͺ…λ£Œν•œ μ€€λΉ„ 사항을 κ°–μΆ”κ³  있으며, 쀑볡이 μ΅œμ†Œν™”λœ ν…ŒμŠ€νŠΈλŠ” 이름을 짓기가 쉽고 ν…ŒμŠ€νŠΈ λͺ©μ μ΄ λΆ„λͺ…ν•˜κ²Œ λ“œλŸ¬λ‚œλ‹€.

πŸ“š ν‘œν˜„μ΄ μ•„λ‹Œ 정보λ₯Ό μœ„ν•œ ν…ŒμŠ€νŠΈβ€‹

  • ν…ŒμŠ€νŠΈκ°€ μ‹œμŠ€ν…œμ˜ λ‹€λ₯Έ λΆ€λΆ„μ—μ„œ ν‘œν˜„λ˜λŠ” 값에 영ν–₯을 λ°›λŠ” ꡬ쑰가 되면 κ·ΈλŸ¬ν•œ 뢀뢄에 λŒ€ν•œ μ˜μ‘΄μ„±μ΄ 생기고, κ²°κ΅­ μ˜μ‘΄ν•˜λŠ” 뢀뢄이 λ³€κ²½λ˜λ©΄ ν…ŒμŠ€νŠΈλ„ 꺠질 것이닀.
  • μ•„λž˜λŠ” νŠΉμ • 이메일 μ£Όμ†Œμ— ν•΄λ‹Ήν•˜λŠ” Customerλ₯Ό μ°ΎλŠ” 것이닀.
public interface CustomerBase {
// 고객을 찾을 수 μ—†λ‹€λ©΄ null을 λ°˜ν™˜
Customer findCustomerWithEmailAddress(String emailAddress);
// ...
}

// ν…ŒμŠ€νŠΈ
allowing(CustomerBase).findCustomerWithEmailAddress(theAddress);
wil(returnValue(null)); // 널 λ°˜ν™˜
  • μ—¬κΈ°μ„œ null을 μ‚¬μš©ν•˜λŠ”λ° λ¬Έμ œμ μ€ null이 무엇을 μ˜λ―Έν•˜κ³  μ–Έμ œ μ μ ˆν•œμ§€ κΈ°μ–΅ν•΄μ•Ό ν•œλ‹€λŠ” 것이닀. 즉, ν…ŒμŠ€νŠΈκ°€ 자기 μ„œμˆ μ μ΄μ§€ μ•Šλ‹€. 또 λ‹€λ₯Έ 점은 μœ μ§€ λ³΄μŠ€μ— λ“œλŠ” λΉ„μš©μ΄λ‹€. μ–Έμ  κ°€ null μ°Έμ‘°κ°€ λ°œμƒν•˜λŠ” 곳을 CustomerBaseκΉŒμ§€ μ°Ύμ•„ λ‚΄λ €κ°€μ•Ό ν•  것이닀.
  • λŒ€μ‹  ν…ŒμŠ€νŠΈμ— '고객을 찾을 수 μ—†μŒ'의 자체적인 ν‘œν˜„μ„ null λ¦¬ν„°λŸ΄ λŒ€μ‹  μ μ ˆν•œ 이름을 μ§€λ‹Œ 단 ν•œ 가지 μ œμ•½μœΌλ‘œ λΆ€μ—¬ν•œλ‹€λ©΄ μ΄λŸ¬ν•œ νž˜λ“€κ³  λ‹¨μ‘°λ‘œμš΄ 일을 ν”Όν•  수 μžˆμ„ 것이닀.
public static final Maybe<Customer> NO_CUSTOMER_FOUND = Maybe.nothing();
  • ν…ŒμŠ€νŠΈλŠ” 객체 간에 μ „λ‹¬λœ 정보 μΈ‘λ©΄μ—μ„œ μž‘μ„±ν•΄μ•Ό ν•˜λ©°, ν•΄λ‹Ή 정보가 μ–΄λ–»κ²Œ ν‘œν˜„λ˜λŠ”μ§€ μΈ‘λ©΄μ—μ„œ μž‘μ„±ν•΄μ„œλŠ” μ•ˆ λœλ‹€.
  • NO_CUSTOMER_FOUND 같은 μ€‘μš”ν•œ 값은 μƒμˆ˜λ‘œ ν•œ κ³³μ—μ„œ μ •μ˜ν•΄μ•Ό ν•œλ‹€.

πŸ“š μ •ν™•ν•œ 단정​

  • ν…ŒμŠ€νŠΈμ—μ„œλŠ” ν…ŒμŠ€νŠΈ 쀑인 μ‹œλ‚˜λ¦¬μ˜€μ™€ 관련이 μžˆλŠ” 단정에 μ§‘μ€‘ν•œλ‹€. ν…ŒμŠ€νŠΈ μž…λ ₯ 값에 μ’Œμš°λ˜λŠ” 단정 값은 μžμ œν•˜κ³  λ‹€λ₯Έ ν…ŒμŠ€νŠΈμ—μ„œ κ²€μ‚¬ν•œ ν–‰μœ„λ₯Ό μž¬λ‹¨μ •ν•˜λŠ” 것도 μžμ œν•œλ‹€.
  • ν…ŒμŠ€νŠΈ 단정은 λŒ€λΆ€λΆ„ 동일성을 λ‹¨μˆœνžˆ ν™•μΈν•˜λŠ” 것에 ν•΄λ‹Ήν•œλ‹€.
  • κ²°κ³Όκ°€ 더 λ³΅μž‘ν•΄μ§ˆ 수 μžˆλŠ” 방법
  1. κ²°κ³ΌλŠ” ꡬ쑰적인 κ°’ νƒ€μž…μœΌλ‘œ μ •μ˜λ  수 μžˆλ‹€. μ΄λŠ” μš°λ¦¬κ°€ λ‹¨μ •ν•˜κ³ μž ν•˜λŠ” 속성을 직접 μ°Έμ‘°ν•  수 있기 λ•Œλ¬Έμ— μ΄ν•΄ν•˜κΈ° 어렡지 μ•Šλ‹€.
  2. ν…μŠ€νŠΈ λ¬Έμžμ—΄μ— κ΄€ν•΄ 단정해야 ν•  λ•Œκ°€ μžˆλ‹€. λ•Œλ•Œλ‘œ ν…μŠ€νŠΈκ°€ μ •ν™•νžˆ 무엇이어야 할지 μ•Œκ³  싢은데, νŠΉμ • λ©”μ‹œμ§€λ₯Ό μ°Ύμ•„μ•Ό ν•  λ•Œκ°€ κ·Έλ ‡λ‹€. μ΄λ•Œ 단지 핡심 정보가 포함돼 μžˆλŠ”μ§€λ§Œ μ•Œκ³  μ‹Άλ‹€λ©΄ λ‹€μŒκ³Ό 같이 μž‘μ„±ν•˜λ©΄ λœλ‹€.
assertThat(failureMessage, allOf(containsString("strikePrice=92"), containsString("id=FGD.430")));
  • 이 λͺ¨λ“  λ¬Έμžμ—΄μ΄ failureMessage의 얻ㄴ가에 λ‚˜νƒ€λ‚˜λŠ”μ§€λ§Œ λ‹¨μ •ν•œλ‹€. μ΄λ ‡κ²Œλ§Œ 해도 ν™•μ‹ ν•  수 있으며, μ€‘μš”ν•˜λ‹€κ³  여겨진닀면 λ‹€λ₯Έ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•΄ λ©”μ‹œμ§€κ°€ μ˜¬λ°”λ₯Έ 포맷으둜 돼 μžˆλŠ”μ§€ 확인할 수 μžˆλ‹€.

πŸ“š μ •ν™•ν•œ μ˜ˆμƒ ꡬ문​

  • 각 λͺ© 객체 ν…ŒμŠ€νŠΈμ—μ„œλŠ” ν…ŒμŠ€νŠΈ λŒ€μƒ 객체와 κ·ΈλŸ¬ν•œ 객체의 이웃 κ°„μ˜ μƒν˜Έ μž‘μš©μ˜ κ΄€λ ¨ μ„ΈλΆ€ 사항을 λͺ…μ‹œν•΄μ•Ό ν•œλ‹€. ν•œ 객체에 λŒ€ν•œ 일련의 λ‹¨μœ„ ν…ŒμŠ€νŠΈλŠ” ν•΄λ‹Ή 객체가 μ‹œμŠ€ν…œμ˜ λ‹€λ₯Έ λΆ€λΆ„κ³Ό ν†΅μ‹ ν•˜λŠ” 데 ν•„μš”ν•œ ν”„λ‘œν† μ½œμ„ κΈ°μˆ ν•œλ‹€.

🎈 μ •ν™•ν•œ λ§€κ°œλ³€μˆ˜ 맀칭​

  • λ©”μ„œλ“œμ— μ „λ‹¬ν•˜λŠ” 값에 κ΄€ν•΄μ„œλ„ 정확함을 κΈ°ν•˜κ³ μž ν•œλ‹€.

🎈 ν—ˆμš©κ³Ό μ˜ˆμƒβ€‹

  • jMock은 λͺ¨λ“  μ˜ˆμƒ ꡬ문이 ν…ŒμŠ€νŠΈ 도쀑에 μΆ©μ‘±λ˜μ§€λ§Œ ν—ˆμš©μ€ μΌμΉ˜ν•˜κ±°λ‚˜ 그렇지 μ•Šμ„ μˆ˜λ„ μžˆλ‹€. μ΄λŸ¬ν•œ κ΅¬λΆ„μ˜ μš”μ μ€ νŠΉμ • ν…ŒμŠ€νŠΈμ—μ„œ μ€‘μš”ν•œ 뢀뢄을 κ°•μ‘°ν•˜λŠ” 데 μžˆλ‹€.
  • μ˜ˆμƒ ꡬ문은 ν…ŒμŠ€νŠΈν•˜λŠ” ν”„λ‘œν† μ½œμ— 핡심적인 μƒν˜Έ μž‘μš©μ„ μ„œμˆ ν•œλ‹€. (이 λ©”μ‹œμ§€λ₯Ό 객체에 보내면 ν•΄λ‹Ή 객체가 이 λ‹€λ₯Έ λ©”μ‹œμ§€λ₯Ό 이 이웃 객체에 보낼 κ²ƒμœΌλ‘œ μ˜ˆμƒν•œλ‹€.)
  • ν—ˆμš©μ€ ν…ŒμŠ€νŠΈν•˜λŠ” μƒν˜Έ μž‘μš©μ„ λ³΄μ‘°ν•œλ‹€. ν…ŒμŠ€νŠΈ ν•˜κ³  싢은 ν–‰μœ„μ— λŒ€ν•΄ 객체가 μ˜¬λ°”λ₯Έ μƒνƒœλ₯Ό μ§€λ‹ˆκ²Œ ν•˜κΈ° μœ„ν•΄ ν—ˆμš©μ„ μŠ€ν…μœΌλ‘œ μ‚¬μš©ν•˜κΈ°λ„ ν•œλ‹€. μ΄λŸ¬ν•œ λ©”μ‹œμ§€λŠ” λ‹€λ₯Έ ν…ŒμŠ€νŠΈμ—μ„œ λ‹€λ£° 것이닀.
  • 이 κ·œμΉ™μ€ ν…ŒμŠ€νŠΈλœ κ°μ²΄μ—μ„œ ν…ŒμŠ€νŠΈλ₯Ό λΆ„λ¦¬ν•˜λŠ” 데 도움이 λœλ‹€.

🎈 λ¬΄κ΄€ν•œ 객체 λ¬΄μ‹œν•˜κΈ°β€‹

  • μ‹œν—˜ 쀑인 κΈ°λŠ₯κ³Ό λ¬΄κ΄€ν•œ ν˜‘λ ₯ 객체λ₯Ό λ¬΄μ‹œν•΄ ν…ŒμŠ€νŠΈλ₯Ό λ‹¨μˆœν™”ν•  수 μžˆλ‹€.
  • μ΄λ ‡κ²Œ ν•˜λ©΄ ν…ŒμŠ€νŠΈκ°€ λ‹¨μˆœν•΄μ§€κ³  λ¬Έμ œμ— 집쀑할 수 μžˆμœΌλ―€λ‘œ μ€‘μš”ν•œ λ°”κ°€ 무엇이고 μ½”λ“œμ˜ ν•œ 츑면에 생긴 λ³€ν™”κ°€ 관련이 μ—†λŠ” ν…ŒμŠ€νŠΈλ₯Ό κΉ¨λœ¨λ¦¬μ§€λŠ” μ•ŠλŠ”μ§€ μ¦‰μ‹œ μ•Œ 수 μžˆλ‹€.
  • ν•˜μ§€λ§Œ λ¬΄μ‹œλœ κΈ°λŠ₯도 μ–΄λ”˜κ°€μ—μ„œ ν…ŒμŠ€νŠΈν•΄μ•Ό ν•˜κ³  λͺ¨λ“  것을 ν•¨κ»˜ λŒμ•„κ°€κ²Œ ν•˜λŠ” κ³ μˆ˜μ€€ ν…ŒμŠ€νŠΈκ°€ μžˆμŒμ„ 보μž₯ν•΄μ•Ό ν•œλ‹€.

🎈 호좜 μˆœμ„œβ€‹

  • jMockμ—μ„œλŠ” μ–΄λ–€ μˆœμ„œλ‘œλ“  λͺ© 객체λ₯Ό ν˜ΈμΆœν•  수 μžˆλ‹€. 즉, μ˜ˆμƒ ꡬ문은 같은 μˆœμ„œλ‘œ μ„ μ–Έν•  ν•„μš”κ°€ μ—†λ‹€.
  • ν…ŒμŠ€νŠΈμ—μ„œ μƒν˜Έ μž‘μš© μˆœμ„œμ— κ΄€ν•΄ λ§ν•˜λŠ” λ°”κ°€ μ μ„μˆ˜λ‘ κ΅¬ν˜„μ—λŠ” μœ μ—°μ„ μ΄ 더 λŠ˜μ–΄λ‚œλ‹€. μ•„μšΈλŸ¬ ν…ŒμŠ€νŠΈλ₯Ό μ–΄λ–»κ²Œ κ΅¬μ‘°ν™”ν•˜λŠλƒμ—λ„ μœ μ—°μ„±μ΄ 생긴닀.
  • jMockμ—λŠ” 호좜 μˆœμ„œλ₯Ό μ œμ•½ν•˜λŠ” 두 가지 λ©”μ»€λ‹ˆμ¦˜μ΄ μžˆλŠ”λ° ν•˜λ‚˜λŠ” μ‹œν€ΈμŠ€λ‘œ ν˜ΈμΆœν•˜λŠ” 순차 λͺ©λ‘μ„ μ •μ˜ν•˜λŠ” 것이고 λ‹€λ₯Έ ν•˜λ‚˜λŠ” μƒνƒœ κΈ°κ³„λ‘œ μ’€ 더 μ •κ΅ν•œ μˆœμ„œ μ œμ•½μ„ μ„œμˆ ν•  수 μžˆλ‹€.
  • μ‹œν€ΈμŠ€λŠ” μƒνƒœ 기계에 λΉ„ν•΄ μ΄ν•΄ν•˜κΈ°κ°€ 더 κ°„λ‹¨ν•˜μ§€λ§Œ μ œμ•½μ„± 탓에 λΆ€μ μ ˆν•˜κ²Œ μ‚¬μš©ν•  경우 ν…ŒμŠ€νŠΈκ°€ λΆˆμ•ˆμ •ν•΄μ§ˆ 수 μžˆλ‹€. μ‹œν€ΈμŠ€λŠ” μ–΄λ–€ 객체가 이웃 객체에 μ˜¬λ°”λ₯Έ μˆœμ„œλ‘œ 톡지λ₯Ό λ³΄λƒˆλŠ”μ§€ ν™•μΈν•˜λŠ” κ°€μž₯ μœ μš©ν•œ μˆ˜λ‹¨μ΄λ‹€.

🎈 jMock μƒνƒœμ˜ μœ„λ ₯​

  • Statesλ₯Ό μ΄μš©ν•΄ ν…ŒμŠ€νŠΈμ˜ μ„Έ 가지 μ°Έκ°€μž μœ ν˜•, 즉 ν…ŒμŠ€νŠΈ λŒ€μƒ 객체, ν•΄λ‹Ή 객체의 이웃, 그리고 ν…ŒμŠ€νŠΈ 자체λ₯Ό λͺ¨λΈλ§ν•  수 μžˆλ‹€.
  • ν…ŒμŠ€νŠΈ λŒ€μƒ 객체의 μƒνƒœμ— λŒ€ν•΄ μ΄ν•΄ν•œ λ°”λ₯Ό ν‘œν˜„ν•  수 μžˆλ‹€. ν…ŒμŠ€νŠΈλŠ” ν•΄λ‹Ή 객체가 이웃 객체에 μ „λ‹¬ν•˜λŠ” 이벀트λ₯Ό λŒ€κΈ°ν•˜κ³  μžˆλ‹€κ°€ 이벀트λ₯Ό μ΄μš©ν•΄ μƒνƒœ 전이λ₯Ό μΌμœΌν‚€κ³  객체의 ν”„λ‘œν† μ½œμ„ μœ„λ°˜ν•˜λŠ” 이벀트λ₯Ό κ±°λΆ€ν•œλ‹€.
  • StatesλŠ” ν…ŒμŠ€νŠΈμ—μ„œ ν•΄λ‹Ή 객체 κ΄€ν•΄ 찾은 κ΄€λ ¨ 사항을 κΈ°μˆ ν•˜μ§€, 객체 λ‚΄λΆ€ ꡬ쑰λ₯Ό κΈ°μˆ ν•˜μ§€ μ•ˆν”λ‹€. (객체의 κ΅¬ν˜„μ— μ œμ•½ 받지 μ•ŠλŠ”λ‹€.)

🎈 훨씬 더 자유둜운 μ˜ˆμƒ ꡬ문​

  • jMockμ—λŠ” μž„μ˜ μ˜ˆμƒ ꡬ문을 μ •μ˜ν•΄ λΌμ›Œ 넣을 수 μžˆλŠ” 지점이 μžˆλ‹€. 이λ₯Όν…Œλ©΄, μ–΄λ–€ μ ‘κ·Όμž λ©”μ„œλ“œλ₯Ό λ°›μ•„λ“€μ΄κ²Œλ” μ˜ˆμƒ ꡬ문을 μž‘μ„±ν•  수 μžˆλ‹€.
allowing(aPeerObject).method(startsWith("get")).withNoArguments();
  • λ˜ν•œ, 객체 집합 쀑 ν•˜λ‚˜μ— λŒ€ν•œ ν˜ΈμΆœμ„ λ°›μ•„λ“€μ΄κ²Œλ” μ˜ˆμƒ ꡬ문을 μž‘μ„±ν•  수 μžˆλ‹€.
oneOf(anyOf(same(o1), same(o2), same(o3))).method("doSomething");

πŸ“š μ‹€ν—˜μš© μ₯ 객체​

  • μ–΄λŒ‘ν„° μ½”λ“œμ— λŒ€ν•œ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•  λ•Œ κ°€μž₯ μ‰¬μš΄ 접근법은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 도메인 λͺ¨λΈμ˜ νƒ€μž…μ„ μ‚¬μš©ν•˜λŠ” 것이닀.
  • ν•˜μ§€λ§Œ, μ΄λ ‡κ²Œ ν•˜λ©΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜κ³Ό μ–΄λŒ‘ν„° 도메인이 κ±Έν•©λ˜κΈ° λ•Œλ¬Έμ— ν…ŒμŠ€νŠΈκ°€ λΆˆμ•ˆμ •ν•΄μ§„λ‹€. 이 경우 관심사λ₯Ό λΆ„λ¦¬ν•˜μ§€ μ•Šμ•˜μœΌλ―€λ‘œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ λͺ¨λΈμ„ λ³€κ²½ν–ˆμ„ λ•Œ ν…ŒμŠ€νŠΈλ₯Ό 잘λͺ» κΉ¨λœ¨λ¦¬λŠ” μœ„ν—˜μ΄ μ΄ˆλž˜λœλ‹€.
  • 또 λ‹€λ₯Έ λ¬Έμ œλŠ” ν…ŒμŠ€νŠΈ 가정이 깨질 λ•Œ 이λ₯Ό νŒŒμ•…ν•˜κΈ°κ°€ 더 μ–΄λ ΅λ‹€λŠ” 것이닀. ν…ŒμŠ€νŠΈ μ€‘μ—μ„œ μž‘λ™μ΄ 멈좘 것이 μžˆλ‹€λŠ” 것과 μ€‘μš”ν•œ κΈ°λŠ₯이 닀뀄지지 μ•Šμ•˜λ‹€λŠ” 사싀을 μš°λ¦¬μ—κ²Œ μ•Œλ €μ£ΌλŠ” 것이 아무것도 μ—†λ‹€.