Image by pikisuperstar on Freepik
하나의 큰 기능을 구현하면서 단계 단계 내부 구현을 테스트 코드로 만들어 검증해 보았다. 확실히 큰 덩어리를 테스트 하는 것 보다 일이 단순하고 점진적으로 완성해 나갈 수 있어서 좋다. 그런데 나중에 기능 구현을 완성하고 보니 잠깐 몇 일 사이에 내부 구현 방향이 처음과 조금씩 달라졌다. 구현하면서 설계가 다듬어졌기 때문이다. 자연스레 단계 단계를 테스트한 코드가 실패한다. 그때는 유용했고 궁극적으로 최종 기능을 완성하는데 기여했지만 이제는 쓸모없는 것이 되어버렸다.
테스트 코드에서 이런 현상이 나타나는 것을 리팩토링 내성이 약하다고 표현한다. 즉 코드 내부구현을 고치면 테스트가 함께 깨지는 것이다. 그래서 테스트코드를 계속해서 함께 변경해 주어야 하는데 그러기 쉽지 않으니 깨지는 테스트 코드를 방치하게 되고 이로 인해 거짓 실패 알람을 개발 팀이 자주 접하게 된다. 그리고 팀은 점점 실패 알람이 양치기 소년 처럼 되어 나중에 진짜 실패에 귀 기울이지 못하는 심각한 문제를 야기한다.
그래서 테스트 코드에서 내부 구현에 대해서는 알지 못하도록 하고 소프트웨어의 외부에 드러난 동작을 테스트 하라고 권고하기도 한다. 좋은 조언이다. 그러나 유연할 필요는 있겠다. 테스트 코드를 작성하는 이유가 테스트 코드를 지속적으로 유지하면서 소프트웨어에 변경이 있을 때마다 오류 여부를 확인하고 오류가 있으면 알려주는 역할도 있지만 약간 다른 목적으로 테스트 코드를 작성할 때도 있기 때문이다.
때때로 테스트 코드가 코드 설계와 구현 과정을 함께하는 코파일럿 같은 존재일 때도 있다. 무슨말이냐면 테스트 코드를 유지하는게 목적이 아니라 현재의 구현에 도움이 받기 위한 임시적인 존재로서 필요하단 뜻이다. 설명이 적절한지 모르겠지만 이런 경우에는 테스트 코드 자체가 의미있는 것이 아니라 테스트 코드를 작성하고 실행하며 학습한 것 그리고 그로인해 안정화된 제품 코드가 중요한 것이다. 이런 경우에 테스트 코드는 삭제해도 아쉽지 않은 존재가 된다.
마치 최근에 실용주의 프로그래머 책에서 읽은 프로토타이핑에 대한 설명과 똑 닮아 있다.
"프로토타입의 가치는 생성된 코드에 있는 것이 아니라 이를 통해 배우게 되는 교훈에 있다." <실용주의 프로그래머>
오늘은 이 코파일럿 같은 내부 구현을 테스트한 코드를 과감히 삭제해보았다. 예전에는 이런 유형의 테스트 코드를 지우지는 못하고 잠시 실행되지 않도록 비활성해두고 참고용이란 주석을 달아놓기 까지 했는데 프로젝트 기간 내내 다시는 보지 않고 실행시킬 필요도 없는 경험을 해보고 오늘은 삭제해 보았다. 이런 시기적절한 행동을 해야하는 엔지니어링이 참 재미있다.