Test Driven Development (TDD)은 개발이 이루어진 다음 그것이 계획대로 잘 완성되었는지 테스트 케이스를 작성하고 테스트하는 타 방식과는 달리, 테스트 케이스를 먼저 작성한 다음 테스트 케이스에 맞추어 실제 개발 단계로 이행하는 개발방법론을 말한다.
가장 큰 장점은 높은 퀄리티의 소프트웨어를 보장한다는 것이다. 그렇다면 높은 퀄리티의 소프트웨어는 어떻게 정의할 수 있을까? 참고한 포스트에서는 이를 아래와 같이 3가지로 정의했다.
1. 절대 에러나 버그가 없을 것.
2. 추가적인 요구사항이 있을 때 손쉽게 그 요구사항을 반영할 수 있을 것.
3. 유지보수에 용이할 것.
TDD는 코드의 재사용성을 보장할 것을 명시하므로 TDD를 통한 소프트웨어 개발 시 기능별 철저한 모듈화가 이뤄진다. 이는 종속성과 의존성이 낮은 모듈로 조합된 소프트웨어 개발을 가능하게 하며 필요에 따라 모듈을 추가하거나 제거해도 소프트웨어 전체 구조에 영향을 미치지 않게된다.
TDD는 테스트 코드를 먼저 작성하기 때문에 내가 지금 무엇을 해야하는지 분명히 정의하고 개발을 시작하게 된다. 또한 테스트 시나리오를 작성하면서 다양한 예외상황에 대해 생각해 볼 수 있으며 이는 완성도 높은 설계로 이어진다. 따라서 매우 크리티컬한 예외상황이 생길 확률이 극히 낮으며, 개발진행 중 소프트웨어의 전반적인 설계가 변경되는 일을 방지할 수 있다.
이는 통합 테스팅이 아닌 유닛 테스팅을 하는 이점이기도 하다. 우리는 각각의 모듈 별로 테스트를 자동화 할 수 있는 코드가 없다면 특정 버그를 찾기 위해서 모든 레벨의 코드들을 살펴봐야 한다. 예를 들어 사용자의 데이터가 잘못 나온다면 DB의 문제인지, 비지니스 레이어의 문제인지 UI단의 문제인지 실제 모든 레이어들을 전부다 디버깅 해야하지만, TDD의 경우 자동화 된 유닛테스팅을 전재하므로 특정 버그를 손 쉽게 찾아낼 수 있다.
주로 SI 프로젝트를 진행하다 보면 어떤 요소들이 테스트 되었는지 테스트 정의서를 만들곤 한다. 이것은 단순 통합테스트 문서에 지나지 않는다. 즉, 내부적인 하나하나의 로직들이 어떻게 테스트 되었는지 제공할 수 없다. 하지만 TDD를 하게 될 경우 테스팅을 자동화 시킴과 동시에 보다 정확한 테스트 근거를 산출할 수 있다.
개발이 완료된 소프트웨어에 어떤 기능을 추가할 때 가장 우려되는 점은 해당 기능이 기존 코드들에 어떤 영향을 미칠지 모른다는 점이다. 따라서 단순한 기능이라도 추가 된 후에는 모든 기능들을 처음부터 테스트 해야한다. 하지만 TDD의 경우 자동화된 유닛 테스팅을 전재하므로 이러한 테스트 기간 역시 획기적으로 단축시킬 수 있다.
실패하는 테스트 케이스를 먼저 만든다. 실패하는 테스트 케이스를 만들 때는 프로젝트 전체 기능에 대하여 모든 테스트 케이스를 작성하는 것이 아니다. 가장 먼저 구현할 기능의 테스트 케이스를 작성한다.
실패한 테스트 케이스를 통과시키기 위하여 코드를 작성하여 테스트를 통과시킨다.
구현한 코드에 중복이 있거나, 개선할 수 있다면 리팩토링을 진행한다. 리팩토링을 진행하고 나서도 테스트 케이스가 성공하는지 확인한 후 다시 실패(Red)로 돌아가서 다른 기능 구현을 위한 테스트 케이스를 작성한다.
TDD는 개발하는 과정에서 테스트를 내포하고 있기 때문에 코드 작성 단계에서 버그를 줄여나갈 수 있다. 또한 TDD에 사용할 단위테스트를 실행하기 위해 기능을 아주 작은 단위의 함수로 쪼개게 되는데, 이로 인해 종속성과 의존성이 낮은 모듈로 조합된 코드를 만들게 된다.
TDD는 분명 좋은 품질의 코드를 만들 수 있는 방법론이지만 코드의 생산성이 크게 저하된다. 비즈니스 로직, 각종 코드 디자인에도 많은 시간이 소요되는데 테스트 코드까지 작성하기란 벅찬일 일수있다. 코드 품질보다는 빠른 생산성이 요구되는 상황에서 TDD는 걸림돌이 될 수 있다.
또한 진입 장벽이 존재한다. 어떤 부분을 테스트해야 할지, 어떻게 테스트해야 할지, 어떤 프레임워크를 써야 할지 등 여러 부분에 대한 학습이 필요하고 적응하는 데 시간이 소요된다
위의 그래프를 보면 TDD 를 적용하는게 모두 좋은건 아니다. t1 시점이 오기전까지 추가적인 기능 없이 운영이 된다면 TDD 미사용이 더 비용이 낮고 시간도 아낀다.t1 시점이 와도 TDD 가 좋은건 아니다. 이전 격차만큼 t2 까지는 TDD 미사용이 더 비용이 저렴하다.
지속적인 기능 확장이 되면 TDD 미사용이 엄청나게 개발 속도가 느려지게 된다. 원인은 이전 기능에 대한 테스트가 전혀 없기 때문에 기능을 모두 재검증 해야 한다. 그에 반해 TDD는 테스트 자동화로 손쉽게 피드백을 받아 확인할 수 있다. 개발자라면 지속적인 기능 확장이 있는지 파악하고 TDD를 적용할지 판단해야한다.
하지만 TDD를 더 잘할 수 있게 되거나, TDD와 관련된 도구가 발달하여 TDD를 사용할 때 드는 비용 자체를 줄 일 수 있다면, 즉 t1, t2 시점이 더 앞으로 땡겨진다면 TDD 도입을 적극적으로 생각해보면 된다.
아주 작은 토이프로젝트에 테스트코드를 작성해가며 코드를 작성해 보았다.해당 경험이 전부였지만, 아주 작은 단위테스트에서 ToDoList를 파편화 시키고 해당 단위테스트를 통과 시키기 위해 코드를 작성하는 로직을 적용해보았다. 이러한 규칙이 나중에 꽤나 힘들게 발견했을 구현 실수를 조기에 발견할 수 있게 해주는 경우가 많았다. 또한 TDD를 통해 테스트 단위로 코드를 짜는 것이 SOLID원칙을 지키는 것과 코드의 재사용성 또한 용이하게 해주었다.
결과적으로 개발에 드는 시간을 장기적으로 보았을 때 줄일 수 있고, 테스트 결과를 Green으로 바꾸는 행위 자체만으로 ToDoList를 성취해 나가듯이 단위 업무에대한 성취감 덕분에 프로그래밍을 더 재미있게 만들어 주는 것 같았습니다.
아쉬운점은 책의 번역이 다소 직관적이지 못했고, 실무코드와 예제 사이에는 차이가 있을 수 있다는 점, 사용 언어에 따른 테스트 방식의 변화에 대해선 언급이 없다는 점이었습니다.
또한 네트워크 관련 테스트, 데이터베이스 테스트 처럼 외부 의존성이 포함된 테스트 들에 대해선
어떻게 해야하는지 간단한 언급뿐이어서 다른 자료를 찾아보고 활용해야할것 같습니다.
이 링크 에서는 토스에서는 어떻게 100퍼센트 Coverage로 TDD를 달성했는지 나온다. 해당 영상을 참고하셔 다시한번 TDD를 적용 해보는 경험을 해봐야 할 것 같다.