결과까지 스스로 판정하는 자동화된 테스트를 갖춘 코드. 사람이 출력을 눈으로 확인할 필요 없이 명령 하나로 "모두 통과"를 알 수 있어야 한다. 리팩터링의 전제 조건이다.
왜 가치 있나
- 버그가 생기면 몇 분 전 변경에서 찾으면 되니 디버깅 시간이 크게 준다
- 테스트가 쌓이면 잘 되던 기능이 깨지는 회귀 버그(Regression)를 바로 잡는다
- 테스트를 먼저 쓰면 완료 시점이 분명해진다(테스트 주도 개발 (TDD))
테스트 구조
- 준비-수행-단언(Arrange-Act-Assert), 또는 같은 뜻의 Given-When-Then: 필요한 데이터를 만들고, 동작을 실행하고, 결과를 검사한다
- 픽스처(Fixture, 테스트에 필요한 데이터와 객체)는 테스트끼리 공유하지 않는다. 공유하면 한 테스트가 바꾼 상태가 다른 테스트를 깨뜨린다. 테스트마다 새로 만든다
무엇을 테스트하나
- 실패해야 할 상황에서 정말 실패하는지 확인한다. 일부러 코드를 틀리게 바꿔 테스트가 빨개지는지 본다
- 모든 걸 다 테스트하려다 하나도 못 쓰느니, 위험한 곳에 집중한다. 복잡한 처리, 경계 조건(빈 값, 음수, 범위 끝)이 우선이다
- 외부에서 들어오는 입력은 검증 테스트가 필요하지만, 같은 코드베이스 안의 모듈끼리 서로 반복 검증할 필요는 없다
- 버그 리포트를 받으면 그 버그를 드러내는 테스트부터 쓴다
- 커버리지(Test Coverage)는 테스트하지 않은 곳을 찾는 데만 쓴다. 숫자 자체가 테스트 품질을 말해 주지는 않는다(굿하트의 법칙)
외부 API를 흉내 내는 데는 MSW로 API 모킹, 테스트 종류 사이의 비율은 테스트 피라미드, 나쁜 테스트의 신호는 테스트 냄새 참고. 코드 안에 가정을 남기는 어서션과도 통한다.
출처: 『리팩터링 2판』 마틴 파울러 (원서 Refactoring, 2nd Edition) 4장 · Self Testing Code