3부는 서사가 아니라 카탈로그다. 앞 두 부에서 몸으로 익힌 리듬을 이제 이름 붙은 패턴으로 정리한다. 이 장은 그 출발점 — “그래서 TDD를 한다는 게 정확히 뭘 하는 것인가”에 대한 가장 기본적인 답들이다. 모든 것은 “테스트”라는 단어를 명사로 다시 읽는 데서 시작한다.

flowchart LR
    LIST["테스트 목록"] --> PICK["다음 테스트 하나 선택"]
    PICK --> RED["테스트 우선<br/>실패하는 테스트 작성"]
    RED --> ASSERT["단언 우선<br/>검증부터 거꾸로"]
    ASSERT --> GREEN["통과시키기"]
    GREEN --> REFACTOR["중복 제거"]
    REFACTOR --> LIST

테스트를 명사로 읽는다

테스트(명사). “테스트한다(동사)“는 불안한 활동이지만, “테스트(명사)“는 언제든 눌러 확인할 수 있는 자동화된 검증 절차다. 소프트웨어에서 스트레스를 줄이는 방법은 두 가지 — 검증을 미루거나, 검증을 자동화하거나. TDD는 후자를 택한다. 스트레스가 오를수록 테스트를 더 적게 하고 싶어지지만, 자동화된 테스트가 있으면 스트레스가 오를수록 오히려 테스트를 더 돌리게 된다. 이 방향의 역전이 이 책 전체의 심리적 토대다.

판단 기준: 손으로 눌러 확인하고 싶어지는 순간이 바로 그 확인을 테스트로 박제할 순간이다. 함정: “나중에 테스트로 옮기지”라며 콘솔 출력·디버거로 때우면, 그 확인은 사라지고 매번 다시 손으로 눌러야 한다.

격리된 테스트

격리된 테스트. 테스트끼리 서로 영향을 주면 안 된다. 하나가 다른 하나의 결과에 의존하면, 앞 테스트가 깨질 때 뒤 테스트가 연쇄로 무너져 원인 파악이 어려워진다. 각 테스트는 자기가 필요한 상태를 스스로 만들고, 끝나면 흔적을 남기지 않아야 한다. 이 요구는 뜻밖의 설계 압력을 만든다 — 테스트를 격리하려면 객체들도 서로 느슨하게 결합돼야 하기 때문이다.

판단 기준: 테스트 실행 순서를 뒤섞어도 결과가 같아야 한다. 함정: 공유된 전역 상태나 정적 필드는 격리를 깨는 단골이다 → 픽스처로 상태를 국소화한다(29장).

할 일을 목록으로 외재화한다

테스트 목록. 시작하기 전에, 구현해야 할 테스트를 전부 적는다. 떠오르는 리팩터링과 미처 못 다룬 조건도 여기에 쌓는다. 머릿속에 담지 않고 종이(또는 파일)에 내려놓으면, 지금 이 한 걸음에만 집중할 수 있다. 1장에서 이미 몸으로 본 그 목록이다. 목록은 진행 상황의 지도이자, 언제 멈춰도 되는지를 알려주는 기준이다.

  • 새 오퍼레이션이 필요하면 → 그 오퍼레이션의 빈 껍데기 테스트를 목록에 추가
  • 리팩터링 아이디어가 떠오르면 → 지금 고치지 말고 목록에 적기
  • 목록이 비면 → 그 세션은 끝난 것이다

판단 기준: 목록이 비었을 때가 커밋하고 쉴 자연스러운 지점이다. 함정: 목록에 없던 걸 즉흥적으로 파고들면 지금 걸음과 다음 걸음이 뒤엉킨다.

어느 것을 먼저 쓰는가

테스트 우선. 테스트는 코드를 짜기 “직전”에 쓴다. 코드를 다 짠 뒤엔 테스트를 쓸 마음이 잘 안 든다. 테스트를 먼저 쓰면 세 가지를 얻는다 — 코드가 어떻게 쓰일지(API 설계)를 먼저 결정하고, 언제 끝났는지(완료 기준)를 명확히 하고, 검증 가능한 범위로 스트레스를 낮춘다.

단언 우선. 테스트를 쓸 때조차 순서가 있다. 단언(assert)부터 쓰고 거꾸로 채워 나간다. 검증하고 싶은 최종 결과를 먼저 적으면, 그 결과에 도달하기 위해 무엇이 필요한지가 역으로 드러난다.

@Test
public void testMultiplication() {
    // 1) 단언부터 — 무엇이 참이길 원하는가
    assertEquals(new Money(10, "USD"), result);
    // 2) result는 어디서 오나 → 위로 채운다
    Money result = five.times(2);
    // 3) five는 어디서 오나 → 다시 위로
    Money five = Money.dollar(5);
}

판단 기준: “이 테스트가 끝났을 때 무엇이 참이면 만족하는가”를 먼저 문장으로 적을 수 있으면 단언 우선이 자연스럽게 굴러간다. 함정: 셋업부터 쓰기 시작하면 목적지 없이 준비만 하다 테스트가 비대해진다.

어떤 값을 넣는가

테스트 데이터. 테스트에 쓰는 데이터는 읽는 사람이 의도를 파악하기 쉬운 값이어야 한다. 곱셈을 검증하는데 2 × 2 = 4를 쓰면 곱인지 합인지 구분이 안 된다. 2 × 3 = 6처럼 연산이 드러나는 값을 고른다. 실제 운영 데이터를 흉내 낼 필요는 없다 — 테스트는 예시이지 표본이 아니다.

명백한 데이터. 기대값을 계산해 숨기지 말고, 식 그대로 드러낸다. assertEquals(4, ...)보다 의도가 보이도록 쓰되, 매직 넘버의 출처가 테스트 안에서 자명하게 한다. 데이터에 담긴 의도가 곧 테스트의 문서다.

# 나쁨 — 3이 왜 3인지, 987이 어디서 왔는지 숨어 있다
assert franc.times(3).amount == 987
 
# 좋음 — 식을 그대로 드러내 의도가 자명하다
rate = 329
assert franc.times(3).amount == 329 * 3

판단 기준: 처음 보는 사람이 기대값의 근거를 테스트 안에서 바로 읽어낼 수 있으면 명백한 데이터다. 함정: 계산을 미리 해서 상수만 박으면 나중에 그 숫자가 맞는지 아무도 검증 못 한다 — 테스트가 또 하나의 미스터리가 된다.

다음장으로 26장