2부가 시작된다. 만들 물건은 자동화된 테스트 프레임워크 xUnit이다. 그리고 이 부의 전부를 관통하는 역설이 첫 문장부터 나온다 — 테스트 프레임워크를 TDD로 만들고 싶은데, 그 프레임워크를 테스트해 줄 프레임워크가 아직 존재하지 않는다. 닭이 없는데 달걀을 부화시켜야 하는 상황이다.
부트스트래핑 딜레마
1부에서는 JUnit이라는 다 만들어진 도구가 초록/빨강 막대를 대신 켜 줬다. 지금은 그 도구 자체를 만드는 중이라 기댈 곳이 없다. 벡의 해법은 정직하다 — 프레임워크가 스스로를 검증할 수 있을 만큼 자랄 때까지는 사람 눈으로 직접 확인한다. print로 결과를 찍고 눈으로 읽는다. 부끄러운 방법이 아니라 부트스트래핑의 정석이다. 컴파일러도 처음엔 다른 언어로 쓰이고, 자기 자신을 컴파일할 수 있게 되는 순간 독립한다. 우리도 그 순간을 향해 간다.
판단 기준: 검증 수단이 없을 때는 “완벽한 자동 검증”을 기다리지 말고 지금 가능한 가장 단순한 확인(출력을 눈으로 읽기)으로 첫 초록을 만든다. 함정: 도구가 없다고 설계부터 완성해두려 하면 첫 걸음을 못 뗀다. 프레임워크는 자기가 자랄 만큼만 자란 뒤 스스로를 검증하기 시작한다.
무엇을 테스트할 것인가 — WasRun
첫 목표는 아주 작다. “테스트 메서드가 실제로 불렸는가”를 확인하는 것. 이걸 확인하려면 불렸는지 기록해 두는 대상이 필요하다. 그래서 WasRun — 실행되었는지(wasRun) 플래그를 들고 있는, 오직 관찰당하기 위한 테스트 대상을 만든다. 그리고 그것을 실행해 줄 TestCase가 있다. TestCase("testMethod")처럼 메서드 이름을 문자열로 받아 두었다가, run()이 그 이름으로 실제 메서드를 호출한다.
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로 읽지만, 이 프레임워크가 조금 더 자라면 자기 자신을 실행 대상으로 삼아 이 print를 assert로 바꿔 낼 수 있게 된다. 그게 부트스트래핑이 완성되는 순간이다.
이 장이 남긴 할 일 목록
테스트 메서드 호출하기먼저 setUp 호출하기는 아직 — 다음 장- setUp 먼저 부르기
- 나중에 tearDown 부르기
- 테스트 메서드가 실패해도 tearDown 부르기
- 여러 테스트 실행하기
- 수집된 결과 출력하기 (“5개 실행, 2개 실패”)
판단 기준: 목록의 첫 줄만 초록으로 만들었다. 나머지는 다 적어두고 손대지 않는다 — 지금은 “메서드가 불린다”는 사실 하나만 확인되면 충분하다.
다음장으로 19장