Jerry's Log

TDD

contents

테스트 주도 개발(Test-Driven Development, TDD) 은 실제(운영) 코드를 작성하기 전에 실패하는 자동화된 테스트를 먼저 작성하는 소프트웨어 개발 프로세스입니다.

이 프로세스는 전통적인 개발 방식의 역순입니다. 이는 단순한 테스트 기법이 아니라 강력한 설계 방법론입니다. 테스트가 코드 설계를 주도 하며, 코드가 처음부터 모듈화되고, 단순하며, 테스트 가능하도록 강제합니다.

TDD 순환: 레드-그린-리팩토

TDD는 짧고 반복적인 순환(loop)을 기반으로 합니다. 각 순환은 몇 분 정도만 소요되어야 합니다.


1. 🔴 RED: 실패하는 테스트 작성하기

먼저, 아직 존재하지 않는 작은 기능 조각에 대한 자동화된 테스트를 작성합니다.

예시 (Java):

// 간단한 계산기 테스트
@Test
void testAddTwoNumbers() {
    Calculator calc = new Calculator();
    int result = calc.add(2, 3);
    assertEquals(5, result);
}

이 시점에는 Calculator 클래스나 .add() 메서드가 존재하지 않으므로, 이 테스트는 컴파일에 실패합니다 (RED).


2. 🟢 GREEN: 테스트를 통과하는 최소한의 코드 작성하기

다음으로, 테스트를 통과시키기 위해 필요한 최소한의, 가장 간단한 코드를 작성합니다.

예시 (Java):

이전 테스트를 통과시키기 위해 다음과 같이 작성할 수 있습니다.

public class Calculator {
    public int add(int a, int b) {
        return 5; // 테스트를 통과하기 위한 최소한의 코드!
    }
}

이것은 어리석어 보일 수 있지만, calc.add(3, 4)와 같은 다음 테스트를 작성하도록 강제합니다. 이 새 테스트는 실패할 것이고(RED), 이로 인해 return 5;return a + b;라는 실제 해결책으로 변경하게 됩니다(GREEN). 이 과정을 삼각측량(triangulation) 이라고 부릅니다.


3. ♻️ REFACTOR: 코드 정리하기

이제 테스트가 통과하므로, 당신은 안전망을 갖게 되었습니다. 기능이 명세대로 작동한다는 것을 압니다. 이제 자신감을 가지고 코드를 정리할 수 있습니다.

이 단계가 끝나면, 즉시 새로운 RED 테스트를 작성하며 이 순환을 반복합니다.


💡 TDD의 이점

  1. 회귀 방지 안전망: 애플리케이션을 구축하면서 포괄적인 단위 테스트 스위트를 만들게 됩니다. 이는 실수로 무언가를 망가뜨리면 테스트가 즉시 실패할 것을 알기 때문에, 자신감을 가지고 리팩토링하거나 새 기능을 추가할 수 있게 해줍니다.
  2. 좋은 설계를 강제함: 테스트 불가능한 코드(거대하고 복잡한 "갓 클래스(God classes)" 등)를 작성할 수 없습니다. TDD는 자연스럽게 작고, 모듈화되며, 느슨하게 결합된 코드를 작성하도록 강제합니다. 이런 코드는 테스트하기 쉽고, 따라서 유지보수하기도 쉽습니다.
  3. 살아있는 문서: 테스트 스위트 자체가 상세하고 최신 상태인 문서 역할을 합니다. 누구나 테스트를 읽어보고 코드가 모든 시나리오에서 어떻게 동작해야 하는지 정확히 이해할 수 있습니다.
  4. 버그 감소: 버그가 QA 단계에서 몇 주, 몇 달 뒤에 발견되는 것이 아니라, 생성되는 순간에 바로 잡힙니다. 이로 인해 수정이 훨씬 쉽고 저렴해집니다.

🚧 TDD의 어려움

  1. 가파른 학습 곡선: 처음에는 "느리고" 부자연스럽게 느껴집니다. 연습이 필요한 훈련입니다.
  2. 모든 곳에 적용하기 어려움: 비즈니스 로직에는 훌륭하지만, TDD는 GUI(그래픽 사용자 인터페이스)나 데이터베이스 또는 네트워크 API와 같이 외부 시스템과 많이 상호작용하는 코드에는 적용하기 더 어렵습니다. (불가능하지는 않지만, 해당 시스템 _주변_의 로직을 테스트하게 됩니다.)
  3. 나쁜 테스트의 위험: 만약 부실하고 사소한 테스트만 작성한다면, 잘못된 안정감을 얻게 됩니다. 테스트가 너무 "깨지기 쉽다면"(구현에 너무 밀접하게 결합되어 있다면), 리팩토링할 때마다 실패하여 좌절감을 유발할 것입니다.

TDD vs. 전통적인 테스트

전통적인 테스트 테스트 주도 개발 (TDD)
1. 실제 코드를 작성한다. 1. 실패하는 테스트를 작성한다.
2. 코드를 검증하기 위해 테스트를 작성한다. 2. 테스트를 통과시키기 위한 최소한의 실제 코드를 작성한다.
3. 테스트를 실행하고 버그를 고친다. 3. 코드를 리팩토링한다.
목표: 테스트는 코드를 검증하기 위한 사후 활동이다. 목표: 테스트는 개발을 주도하는 설계 활동이다.

references