HJW's IT Blog

TDD : Test Driven Development 본문

개발 개념

TDD : Test Driven Development

kiki1875 2024. 10. 25. 11:05

시작하면서..

우아한 테크코스 프리코스를 진행하던 중, TDD 라는 개념에 대해 알게 되었다. 그래서 이 방법으로 개발을 해보고자 2주차 과제에서 Test 를 먼저 작성하고 넘어가는 과정에서 이상한 점이 눈에 띄었다. Private 으로 선언된 메소드 들은 어떻게 테스트하지?

TDD에 대한 이해도가 낮은 상태에서 적용시키려 하니 발생한 문제였다. 그래서 열심히 찾아본 결과, private 메소드에 대한 테스트를 하고 싶은 이유가 애초에 내가 TLD 방식으로 개발을 했기 때문이라는 결론에 다달았다.

그래서 이번 포스팅은 TDD를 제대로 짚고 넘어가고자 한다.

TDD

TDD란 무엇인가?

Test Driven Development 란, 개발자가 기능 코드를 작성하기 전, 테스트 스크립트를 먼저 작성하는 개발 프로세스이다. 이를 통해 코드의 유효성을 보장하고, 검증할 수 있다. TDD의 cycle은 복잡하지 않다. 원하는 기능에 대한 테스트를 작성 → 테스트를 통과할 수 있는 최소한의 코드 작성 → 리펙토링. 이 프로세스를 제품 혹은 기능이 완성될 때 까지 하는 것이다.

TDD는 Agile 방법론의 일부이다. Agile 방법론은 빠른 개발 주기, 피드백에 기반한 수정, 유연한 대응 등의 특징이 있다. TDD 에선, 각 개발 라운드가 명확하며, 테스트 가능한 목표를 잡고 개발을 시작하게 된다.

TDD는 Red-Green Cycle 이라고도 불리는데, 각각 Test = Red, Code = Green 이다.

  • 테스트 작성 (Red) : 특정 기능과 같은 핵심 요소에 대한 단위 테스트 를 작성한다. 아직 기능을 구현하기 전이므로, 해당 테스트는 반드시 실패해야 한다.
  • 코드 작성 (Green) : 테스트를 통과할 수 있는 최소한의 초드를 작성한다. 이 때, 테스트를 통과하기 때문에 Green 단계라 부른다
  • 리팩토링 : 코드가 테스트를 통과하게 되면, 코드를 더 효율적이고 설계 요구 사항에 맞게 개선한다. 이때, 기능의 동작을 바꾸지 않으면서 코드를 정리하는것이 핵심이다.

왜 TDD 를 사용하나요?

TDD 의 이점은 여러가지가 있다. 특히, 협업을 촉진하는데에 큰 역할을 하며, 제품과 목표에 대한 공유된 이해를 형성한다. TDD 개발 단계를 따르게 되면, 명확한 목표가 정의되어, 무엇을 달성해야 하는지가 명확해진다. 즉, 요구사항을 충실하게 따르는 어플리케이션을 개발할 수 있는 것이다.

이 외에도 TDD는 여러가지 이점을 가지고 있다.

  • Improved Design and Architecture : 테스트를 추가할 때 마다 점진적으로 설계가 개선되기 때문에, TDD는 더 클린하고 유지 보수가 가능한 코드 구조를 촉진한다.
  • Lower Long-Term Costs : 초기 비용은 더 높을 수 있으나, 장기적으로 버그 수정 및 유지보수 비용이 줄어든다.
  • Increased Confidence in Code Changes : 포괄적인 테스트 스위트를 통해 기존 기능이 보호 되고 있음을 확인할 수 있다.
  • Documentation and Specification : TDD의 테스트는 그 자체만으로, 코드 문서화와 명세 역할을 할 수 있으며, 새로운 팀원도 소프트웨어의 기능과 의도를 알기 쉽게 한다.
  • 리펙토링에 대한 자신감 : TDD는 테스트가 코드의 올바른 동작을 보장한다. 그렇기 때문에, 리펙토링을 하며, 코드의 기존 기능이 유지되는지 쉽게 확인할 수 있다
  • 개발 과정이 테스트 코드로 남기 때문에 과거 의사 결정을 쉽게 상기할 수 있다 : TDD를 사용하게 되면, 테스트 코드 작성 과정에서 히스토리가 남는다. 즉, 과거의 코드를 트래킹하며, 어떤 인과관계로 의사결정을 하였는지를 확인할 수 있다.

Best Practise of TDD

TDD 에 대한 개념이 잘못 잡힌 상태로 개발을 시작하게 되면, 위에서 언급한 이점은 커녕 초기 비용만 늘어나는 비효율적인 개발을 하게 된다…

TDD는…

단순하게 시작해야 한다

  • 각 테스트는 코드의 한 측면만을 평가해야 한다. 여러 기능에 대한 평가를 한번에 하게 되면, 실패 원인을 파악하기 어려워 질 수 있다. 예를 들어 API 개발시, 개별 엔드포인트가 올바른 응답 코드를 반환 하는지, 기본 입력 처리가 정상적으로 이루어 지는지를 먼저 테스트 한 후, 더 복잡한 시나리오를 테스트 해야 하는 것이다.

표현력있고, 포괄적으로 작성해야 한다

  • 테스트 프레임워크의 Assertion 라이브러리를 효과적으로 사용해야 하며, 일반적인 조건보다는 상세하고 구체적인 조건을 검증해야 한다. 예를 들어 정렬 기능을 테스트 한다고 해보자. 이때 개발자는 단순히 정렬 여부를 확인하기 보다, 더 구체적으로, 기대한 순서 대로 요소가 정렬되었는지를 확인하는 것이다.

구조화 및 체계화 하기

  • GWT 혹은 3A 패턴을 사용해 테스트 자체를 구조화 하는것이 좋다.
    • G : Given - 특정 상황에서
    • W : When - 특정 데이터가 주어졌을때
    • T : Then - 특정한 결과가 나와야 한다
  • 뿐만 아니라, 앞서 말했듯, 테스트는 그 자체로 하나의 명세서가 될 수 있기 때문에, 테스트 이름도 명확히 짓는것이 좋다. 예를 들어 계산기의 덧셈 기능을 테스트 한다고 하면, testCalculator 보다는 testAdditionReturnsCorrectSum 이 더 올바른 예시인 것이다. 이름만 봐도, 어떤 기능을 하는지, 어떤 결과가 반환되어야 하는지를 유추할 수 있다.

정기적으로 리팩토링 하기

  • 첫 red-green cycle 에선 테스트를 통과하기 위한 코드를 작성했을 것이다. 그렇다면, 그 코드가 효율적이고 올바른 비즈니스 로직을 과연 따를까? 물론 따를 수도 있지만, 개선의 여지가 있다는 점은 분명하다. 그렇기 때문에 여러 사이클을 돌며 정기적으로 리팩토링을 해주어야 한다

포괄적인 테스트 스위트 작성하기

  • 특정 테스트 스위트만 작성하는 것이 아닌, 단위 테스트, 통합 테스트, 종단 간 테스트 등, 포괄적인 테스트 스위트를 작성해야 한다. 또한 단순히 정상적으로 통과되는 테스트 뿐만 아니라, 실패 조건을 테스트 하는 negative test도 작성해야 한다. 특히, edge case 에 대한 테스트도 작성해야 한다.

TDD 의 한계

TDD 는 어디까지나 개발론이기 때문에 비효율적인 상황이라면, TDD 자체가 목적이 되어서는 안된다. 특히 작은 규모의 프로젝트라면, TDD를 사용하였을때의 초기 비용은 사용하지 않았을 때의 초기 비용보다 크다.

테스트는 사람이 작성한다

사실 실수를 예측한다는 것은… 불가능 하지 않을까 싶다. 그렇기 때문에 우리는 테스트를 작성할 때에도, 우리가 인지하고 있는 상황에 대한 테스트만 작성 한다는 것이다. 그렇다면 자연스럽게 상정하지 못한 상황에 대해서는 테스트를 할 수 없다. 하지만 물론 버그를 보다 빠르고 효과적으로 개선할 수 있도록 개발자를 도와주는것은 사실이다.