TDD와 Test 설계 전략
실행 가능한 Test로 Interface를 먼저 사용하고 작은 Feedback Loop로 구현 설계를 발전시킨다.
이 문서의 목차
Overview
TDD는 Test 개수를 늘리는 기법보다, Client 관점의 작은 시나리오를 실행 가능한 Test로 바꾸고 그 Interface를 만족시키는 최소 구현을 만든 뒤 설계를 정리하는 Feedback Loop다.
Interface와 구현 설계
Interface는 Client와의 약속이므로 변경 영향이 외부로 퍼진다. 구현 설계는 약속을 만족시키는 내부 선택이며 대안과 변경 빈도가 더 크다. 기능과 공개 Interface를 유지하며 내부 설계를 바꾸는 것이 Refactoring이고, 공개 약속 자체를 바꾸면 Redesign에 가깝다.
Red–Green–Refactor
flowchart LR; L[시나리오 목록] --> R[Red: 하나의 실패 Test]; R --> G[Green: 최소 구현]; G --> F[Refactor: 중복과 설계 개선]; F --> L
- 필요한 Test 시나리오 목록을 작성한다.
- 가장 작고 구체적인 하나를 실행 가능한 Test로 만든다.
- 새 Test와 이전 Test를 모두 통과시키는 최소 변경을 한다.
- 동작을 유지한 채 이름, 책임, 중복, 의존성을 정리한다.
- 다음 시나리오로 반복한다.
Example
@Test
void 만료된_회원은_로그인할_수_없다() {
Member expired = Member.expired("dev@example.com");
LoginResult result = login.login(expired, "secret");
assertThat(result).isEqualTo(LoginResult.EXPIRED);
}Test는 내부 Private Method 호출보다 운영 API의 첫 Client처럼 공개된 행동을 사용한다. 그래야 Refactoring 때 구현 세부사항 때문에 Test가 무너지지 않는다.
실무에서 발생하는 문제
- 구현을 먼저 만들고 Test만 나중에 붙인 뒤 TDD라 부른다.
- Mock의 호출 순서를 과도하게 검증해 내부 구조 변경을 막는다.
- 한 번에 큰 시나리오를 선택해 실패 원인을 알기 어렵다.
- Test 통과 직후 Refactoring을 생략해 설계 부채를 쌓는다.
Trade-off
불확실한 설계, 복잡한 규칙, 반복 변경에는 빠른 Feedback과 회귀 안전망을 준다. UI Prototype, 외부 System 탐색, Legacy 경계처럼 처음부터 자동화하기 비싼 영역은 Characterization Test나 Spike를 먼저 선택할 수 있다.
Interview Questions
- TDD와 Test-first의 차이를 어떻게 설명할 것인가?
- Test가 구현 세부사항에 결합되었다는 신호는 무엇인가?
- Red 단계에서 Test를 얼마나 작게 만들어야 하는가?
- Refactoring과 Redesign의 경계는 무엇인가?
- Mock 대신 실제 객체를 사용하는 편이 좋은 경우는?
Related Topics
Hexagonal Architecture, Load Testing으로 Test 경계와 비기능 검증을 확장한다.
SOURCE REFERENCES
이 문서의 근거
본문은 Dev Atlas 안에서 완결되며, 검증이 필요할 때만 원문을 확인할 수 있습니다.