2부가 시작된다. 만들 물건은 자동화된 테스트 프레임워크 xUnit이다. 그리고 이 부의 전부를 관통하는 역설이 첫 문장부터 나온다 — 테스트 프레임워크를 TDD로 만들고 싶은데, 그 프레임워크를 테스트해 줄 프레임워크가 아직 존재하지 않는다. 닭이 없는데 달걀을 부화시켜야 하는 상황이다.

부트스트래핑 딜레마

1부에서는 JUnit이라는 다 만들어진 도구가 초록/빨강 막대를 대신 켜 줬다. 지금은 그 도구 자체를 만드는 중이라 기댈 곳이 없다. 벡의 해법은 정직하다 — 프레임워크가 스스로를 검증할 수 있을 만큼 자랄 때까지는 사람 눈으로 직접 확인한다. print로 결과를 찍고 눈으로 읽는다. 부끄러운 방법이 아니라 부트스트래핑의 정석이다. 컴파일러도 처음엔 다른 언어로 쓰이고, 자기 자신을 컴파일할 수 있게 되는 순간 독립한다. 우리도 그 순간을 향해 간다.

판단 기준: 검증 수단이 없을 때는 “완벽한 자동 검증”을 기다리지 말고 지금 가능한 가장 단순한 확인(출력을 눈으로 읽기)으로 첫 초록을 만든다. 함정: 도구가 없다고 설계부터 완성해두려 하면 첫 걸음을 못 뗀다. 프레임워크는 자기가 자랄 만큼만 자란 뒤 스스로를 검증하기 시작한다.

무엇을 테스트할 것인가 — WasRun

첫 목표는 아주 작다. “테스트 메서드가 실제로 불렸는가”를 확인하는 것. 이걸 확인하려면 불렸는지 기록해 두는 대상이 필요하다. 그래서 WasRun — 실행되었는지(wasRun) 플래그를 들고 있는, 오직 관찰당하기 위한 테스트 대상을 만든다. 그리고 그것을 실행해 줄 TestCase가 있다. TestCase("testMethod")처럼 메서드 이름을 문자열로 받아 두었다가, run()이 그 이름으로 실제 메서드를 호출한다.

Refactoring Step 수동 확인 — 플래그를 눈으로 읽는다
1/3
class WasRun:    def __init__(self, name):        self.wasRun = None        self.name = name    def run(self):        self.wasRun = 1        self.testMethod()    def testMethod(self):        passtest = WasRun("testMethod")# 실행 전에는 불리지 않았다print(test.wasRun)test.run()# 실행 후에는 불렸다print(test.wasRun)# None, 그리고 1 — 눈으로 읽어 확인한다. 아직 프레임워크가 아니다.

run()getattr로 이름을 풀어 호출하는 이 한 줄이 xUnit 전체의 씨앗이다. 테스트 프레임워크는 결국 “이름으로 알려진 메서드들을 대신 실행해 주는 기계”이기 때문이다. 지금은 그 결과를 print로 읽지만, 이 프레임워크가 조금 더 자라면 자기 자신을 실행 대상으로 삼아 이 printassert로 바꿔 낼 수 있게 된다. 그게 부트스트래핑이 완성되는 순간이다.

이 장이 남긴 할 일 목록

  • 테스트 메서드 호출하기
  • 먼저 setUp 호출하기 는 아직 — 다음 장
  • setUp 먼저 부르기
  • 나중에 tearDown 부르기
  • 테스트 메서드가 실패해도 tearDown 부르기
  • 여러 테스트 실행하기
  • 수집된 결과 출력하기 (“5개 실행, 2개 실패”)

판단 기준: 목록의 첫 줄만 초록으로 만들었다. 나머지는 다 적어두고 손대지 않는다 — 지금은 “메서드가 불린다”는 사실 하나만 확인되면 충분하다.

다음장으로 19장