private 메서드는 객체지향적인 관점에서 생각한 것이고 노출된 함수 내부에서 접근하는, 클로저안에 숨겨진 함수 역시 동일한 대상이다. 모듈의 외부 인터페이스 뒤에 숨겨진 캡슐화 된 것들을 말한다. 이후 내부 구현이라고 부르겠다. 내부 구현을 테스트해야 할까?
자동화된 테스트는 TDD를 이용할 수도 있고 아닐 수도 있다.
테스트의 유일한 목적은 개발자(프로젝트)에게 도움을 주는 것이다.
내부 구현 테스트는 자유다. 그래서 private 메서드 테스트도 자유다. 하지만 팀 프로젝트 관점에서는 다르다.
자기 만족 함정
내부 구현을 직접적으로 테스트하는 것은 TC의 미래 가치를 떨어뜨린다.
내부 구현 테스트는 인터페이스를 통한 것보다 쉽다.
TC는 적을수록 좋다. 최소의 TC로 최대의 효과를 낼수록 가치는 뚜렷해진다.
불필요한 TC가 많을수록 도움이 되는 TC들의 가치가 떨어진다.
이제 와서 TC를 작성하는 것을 그만 만들거나 지울 수도 없지만, 도움된다고 착각하는 그런 TC가 된다.
모듈과 테스트 케이스의 관계
TC는 모듈의 기능이 일부 변경되거나 추가될 때 기존 시스템에 해당 모듈이 책임져야 할 역할을 충실히 이행할 수 있는지를 보장한다.
그래서 TC는 대상 모듈이 무엇이든 시스템에 맞물려 정상적으로 돌아갈 수 있는지만 확인해야 한다.
TC는 모듈의 사용자다. TC는 바뀔 수 있는 구체적인 모듈이 아니라 바뀌지 않을 모듈의 책임(공개된 톱니바퀴의 모양)을 테스트해야 한다. 모듈이 적절히 수행하도록 메시지를 받는 외부 인터페이스가 자주 바뀐다면 잘못 설계되었다는 신호다.
TC는 모듈의 추상화된 책임만 알고 있으면 된다. 그래서 모듈의 다형성과 자율성을 보장해줘야 한다.
SOLID 중 의존 역전 원칙(DIP)이다. 목적과 효과 면에서 동일하다.
TC 역시 모듈을 사용하는 사용자 입장에서 바라봐야 하고 TC는 모듈의 구체적인 것에 의존하면 안된다. 추상에 의존해야 한다.
그럼에도 불구하고 내부 구현이 테스트 되어야 한다고 생각되는 모듈이 있다면 그건 해당 내부 구현이 독립된 책임을 갖는 별도의 모듈로 추출되어야 한다는 신호일 가능성이 크다.
내부 구현을 클래스나 모듈로 추출해 그 모듈을 사용하는 구조로 변경한다면 추출된 모듈의 TC를 작성해 외부 인터페이스로 테스트할 수 있다. 이것은 내부 구현이 외부 인터페이스로 변경된 좋은 사례에 해당한다.
유용한 TC란
현재를 위해 작성되는 TC는 개발하고 있는 코드의 테스트를 자동화한다.
TDD가 애플리케이션의 구조적인 설계를 직접적으로 도와주지는 않지만 이미 짜인 협력 구조 안에서 각 모듈의 역할과 책임에 필요한 인터페이스를 효과적으로 설계하는 것에는 도움이 된다.
미래를 위한 TC는 대상 모듈의 역할과 책임을 설명할 뿐 아니라 구체적인 사용 방법까지 제시하는 훌륭한 문서가 되어야 한다.
결국 TDD 사이클을 이용하든 하지 않든 TC를 작성하면서 소비한 시간에 대한 충분한 효율을 얻으려면 현재를 위해 TC를 작성하되 개발이 진행되면서 미래를 위한 TC들로 바뀌어야 한다.
적어도 TC가 어떤 도움을 주는지 혹은 어떤 TC가 도움이 되는지를 아는 것은 각 프로젝트 상황에 적합한 TC를 만들 수 있는 최소한의 준비이자 시작점이라고 생각한다.
내부 구현은 공개된 인터페이스를 통해서만 테스트한다
내부 구현이 어디까지 테스트 되고 있는지 파악하기 힘들기 때문이다.
자 여기서 한가지 알려줄 게 있다. 그럴 때 사용하라고 만든 지표가 바로 커버리지(coverage)다.
개발자들이 TC를 잘 작성하고 있는지 감시하려고 만든 수단이 아니다.
테스트의 양적인 품질을 보여주는 지표다. 질적인 품질은 보장해주지 않는다. 전혀 쓸모없는 TC를 만들어도 100% 근접한 결과를 만들 수 있기 때문이다. 커버리지가 TC의 질적인 품질을 보장해주지는 않지만, TC가 모듈의 어느 부분까지 테스트했고 앞으로 어디를 테스트해야 하는지를 확인할 수 있는 척도를 제공해준다.
TDD로 프로젝트를 진행한다고 하면 야심 찬 눈빛으로 커버리지를 몇 프로까지 맞추냐는 질문을 하는 사람들이 가끔 있는데 질문의 의도를 생각하면 참.. 좀 그렇다... TDD 혹은 테스트 자동화를 프로젝트에 진지하게 도입하고 있는 개발자라면 다른 프로젝트의 커버리지보다는 테스트 가능한 것과 테스트 불가능한 것을 어떻게 분리해냈느냐가 더 궁금할 것이다. 여기에서 "어떻게"는 기준, 방법, 합의 측면에서 생각해볼 수 있는데, 이 부분은 더 깊게 들어가지 않겠다.