테스트 구축하기
- 4.1 자가 테스트 코드의 가치
- 4.2 테스트할 샘플 코드
- 4.3 첫 번째 테스트
- 4.4 테스트 추가하기
- 4.5 픽스처 수정하기
- 4.6 경계 조건 검사하기
- 4.7 끝나지 않은 여정
리팩터링을 안전하게 하려면 뭐가 필요할까? 테스트야. 파울러는 이 책이 테스트 책이 아니라고 선을 긋지만, 한 챕터를 통째로 할애할 만큼 중요하다고 봤어. 리팩터링의 전제 조건이 테스트니까.
파울러가 테스트를 진지하게 생각하게 된 계기가 있어. 1992년에 OOPSLA 컨퍼런스에서 누군가 **"클래스마다 테스트 코드를 같이 작성한다"**는 얘기를 듣고, 직접 해봤더니 효과가 엄청났대. 핵심 주장은 이거야 — "프로그래밍 시간의 대부분은 코드를 작성하는 게 아니라 버그를 찾는 데 쓰인다." 버그를 찾는 시간을 줄일 수 있다면, 테스트 작성 시간은 충분히 본전을 뽑아. 마지막으로 테스트를 통과한 시점 이후의 변경만 살펴보면 되고, 리팩터링할 때 실수하면 테스트가 잡아주고, 새 기능을 추가할 때 기존 기능이 깨지지 않았다는 확신을 줘. "테스트 스위트는 강력한 버그 검출기로, 버그를 찾는 데 걸리는 시간을 대폭 줄여준다."
예제로 사용하는 건 지역(province)의 생산자(producer)와 수요(demand)를 관리하는 간단한 비즈니스 로직이야. 수요량, 가격, 생산량을 가지고 이윤과 부족분을 계산하는 거지. 테스트 프레임워크는 Mocha를 쓰는데, 핵심은 어떤 프레임워크를 쓰느냐가 아니라 테스트를 어떻게 구성하느냐야.
첫 테스트는 단순하게 시작해. 부족분(shortfall)을 계산하는 테스트 하나.
describe('province', function() {
it('shortfall', function() {
const asia = new Province(sampleProvinceData());
assert.equal(asia.shortfall, 5);
});
});
테스트 구조는 세 단계야. 설정(setup) — 테스트에 필요한 데이터와 객체를 준비하고, 실행(exercise) — 테스트 대상 코드를 실행하고, 검증(verify) — 결과가 기대한 대로인지 확인해. 이걸 "설정-실행-검증" 패턴이라고 불러. 파울러가 강조하는 습관이 하나 있는데, "테스트를 작성하고 나면 일부러 한 번 실패시켜봐라." 기대값을 틀린 값으로 바꿔서 테스트가 진짜 실패하는지 확인하는 거야. 테스트가 실제로 검증을 하고 있는지 확인하는 게 중요하거든.
하나의 테스트로는 부족해. 이윤(profit) 계산도 테스트해야 하지. 테스트를 추가할 때 "무엇을 테스트할지" 고르는 기준이 중요한데, 파울러의 원칙은 **"위험한 부분에 집중하라"**야. 너무 단순해서 버그가 생길 여지가 없는 코드(getter/setter)는 굳이 테스트하지 않아도 돼. 복잡한 로직, 조건 분기, 계산 로직에 집중해. "완벽하게 만드느라 테스트를 수행하지 못하느니, 불완전한 테스트라도 작성해서 실행하는 게 낫다." 각 테스트마다 픽스처(fixture)를 새로 만드는 것도 중요해. beforeEach를 활용해서 매 테스트마다 독립적인 데이터를 세팅해야 해. 테스트 간에 데이터를 공유하면 한 테스트의 변경이 다른 테스트에 영향을 줄 수 있거든.
읽기만 하는 테스트가 아니라, 데이터를 변경하는 테스트도 필요해.
it('change production', function() {
asia.producers[0].production = 20;
assert.equal(asia.shortfall, -6);
assert.equal(asia.profit, 292);
});
하나의 it절 안에서 검증을 두 번 하는 건 일반적으로 권장되지 않지만, 관련이 깊은 검증이라면 괜찮아. 다만 **"검증과 검증 사이에 실행 코드가 있으면 별도 테스트로 분리하라"**고 조언하지.
그리고 "문제가 생길 수 있는 경계 지점을 테스트하라." 정상적인 케이스는 당연히 테스트하겠지만, 진짜 버그는 경계에서 나와. 컬렉션이 비어 있을 때, 숫자가 0일 때, 숫자가 음수일 때, 문자열이 비어 있을 때. "수요가 마이너스면 어떡하지?" 같은 질문을 스스로에게 던져봐. 경계 조건에서 어떤 일이 일어나야 하는지 생각하는 과정 자체가 가치 있어.
파울러는 이 장이 테스트의 모든 것을 다루지는 않는다고 인정해. 하지만 핵심 원칙은 전달했다고 봐. "버그 리포트를 받으면, 그 버그를 드러내는 테스트부터 작성하라." 버그를 고치기 전에 테스트를 먼저 만들면, 같은 버그가 다시 생기는 걸 막을 수 있어. 테스트 양에 대한 기준은 — "테스트 커버리지 분석은 테스트되지 않은 영역을 찾는 데만 사용하라. 커버리지 숫자 자체를 목표로 삼지 마라." 가장 실용적인 기준은 **"누군가 수정한 뒤 테스트를 돌렸는데 통과했다면, 리팩터링 과정에서 생긴 버그를 테스트가 걸러낼 수 있다는 자신감이 드는가?"**야.
정리
4장 읽고 기억할 거 세 가지:
- "테스트는 리팩터링의 안전망이다." 테스트 없이 리팩터링하는 건 위험해
- "경계 조건을 테스트하라." 정상 케이스보다 경계에서 버그가 나와
- "불완전한 테스트라도 없는 것보다 낫다." 완벽을 추구하다 아무것도 안 하는 게 최악이야