Chapter04 테스트 구축하기
리팩터링을 제대로 하려면 불가피하게 저지르는 실수를 잡아주는 견고한 테스트 스위트가 필요하다. 리팩터링을 하지 않더라도 좋은 테스트를 작성하는 일은 개발 효율을 높여준다.
자가 테스트 코드의 가치
모든 테스트를 완전히 자동화하고 그 결과까지 스스로 검사하게 만들자.
자가 테스트를 만들면 테스트가 컴파일만큼 쉬워진다. 컴파일할 때마다 테스트를 함께 실행하면 디버깅 시간을 크게 줄여줘서 생산성이 상승한다. 자가 테스트 코드 자체뿐 아니라 테스트를 자주 수행하는 습관도 버그를 찾는 강력한 도구가 된다.
테스트 스위트는 강력한 버그 검출 도구로, 버그를 찾는데 걸리는 시간을 대폭 줄여준다.
테스트를 작성하기 가장 좋은 시점은 프로그래밍을 시작하기 전이다. 기능을 추가해야 할 때 테스트부터 작성하자. 순서가 바뀐 듯 들리지만 아니다. 테스트를 작성하다 보면 원하는 기능을 추가하기 위해 무엇이 필요한지 고민하게 된다. 구현보다 인터페이스에 집중하게 된다는 장점도 있다. 또한 코딩이 완료되는 시점을 정확하게 판단할 수 있다. 테스트를 모두 통과한 시점이 코드를 완성한 시점이다.
TDD는 테스트를 작성하고, 테스트를 통과하게끔 코드를 작성하고, 결과 코드를 최대한 깔끔하게 리팩터링하는 과정을 짧은 주기로 반복한다. 이러한 테스트-코딩-리팩터링 과정을 한 시간에도 여러 차례 진행하기 때문에 코드를 대단히 생산적이면서도 차분하게 작성할 수 있다.
코드를 작성할 때 테스트 코드도 같이 작성하자. 간혹 테스트가 갖춰지지 않은 코드를 리팩터링해야 할 때는 곧바로 리팩터링하지 않고 먼저 테스트 코드부터 작성하자.
테스트 추가하기
클래스가 하는 일을 모두 살펴보고 각각의 기능에서 오류가 생길 수 있는 조건을 하나씩 테스트하자. 테스트는 위험 요인을 중심으로 작성해야 한다! 테스트의 목적은 어디까지나 현재 혹은 향후에 발생하는 버그를 찾는데 있다. 따라서 단순히 필드를 읽고 쓰기만 하는 접근자는 테스트할 필요가 없다.
테스트를 너무 많이 만들다 보면 오히려 필요한 테스트를 놓치기 쉽다. 잘못될까봐 가장 걱정되는 영역을 집중적으로 테스트하자.
완벽하게 만드느라 테스트를 수행하지 못하느니, 불완전한 테스트라도 작성해 실행하는 게 낫다.
beforeEach를 사용해서 테스트 관련 버그를 예방하자. 공유 픽스처를 사용하면 다른 테스트에서 객체의 값을 수정하면 다른 테스트가 실패할 수 있다. 테스트의 실행 순서가 결과에 영향을 주면 안된다.
경계 조건 검사하기
범위를 벗어나는 경계 지점에서 문제가 생기면 어떤 일이 벌어지는지 확인하는 테스트도 작성하는게 좋다. 배열이나 객체가 비어있거나 값이 0일 때를 검사해보자. 그리고 음수도 넣어보는 것이 좋다. 경계를 테스트하면서 프로그램에서 이런 특이 상황을 어떻게 처리하는 게 좋을지 생각해볼 수 있다.
문제가 생길 가능성이 있는 경계 조건을 생각해보고 그 부분을 집중적으로 테스트하자.
어차피 모든 버그를 잡아낼 수는 없다고 생각하여 테스트를 작성하지 않는다면 대다수의 버그를 잡을 수 있는 기회를 날리는 셈이다.
테스트가 버그 없는 완벽한 프로그램을 만들 수는 없지만 테스트가 프로그래밍 속도를 높여준다는 사실에는 변함이 없다. 위험한 부분에 집중헤서 테스트 코드를 작성하자. 그리고 코드에서 처리 과정이 복잡한 부분을 찾아보자. 함수에서 오류가 생길만한 부분을 찾아보자. 테스트가 모든 버그를 걸러주지는 못할지라도, 안심하고 리팩터링할 수 있는 보호막은 되어준다. 그리고 리팩터링을 하면서 프로그램을 더욱 깊이 이해하게 되어 더 많은 버그를 찾게 된다.
버그를 발견하면 버그를 잡아내는 테스트 코드를 먼저 작성하자.