본문 바로가기
개념 공부/Test Code

[Test Code / Java] TDD(Test Driven Development)

by clean01 2024. 12. 27.

TDD: Test Driven Development란?

프로덕션 코드보다 테스트 코드를 먼저 작성하여 테스트가 구현 과정을 주도하도록 하는 방법론

즉, 기능 구현보다 테스트를 먼저 구현하고, 기능을 구현하거나 수정할 때 테스트 코드의 도움을 받을 수 있는 방식을 의미한다.

 


Red-Green-Refactor

TDD의 방법으로는 Red, Green, Refactor를 주로 언급한다.

  • Red: 실패하는 테스트를 먼저 작성. → 기능 구현이 돼있지 않기에 테스트가 실패하는 것이 당연하다.
  • Green: 빠른 시간 내에 테스트를 통과하는 최소한의 기능 구현하기. → 초록불을 볼 수만 있다면, 이 단계에서의 구현은 다소 엉터리여도 된다.
  • Refactor: 테스트 통과 상태를 유지하면서 구현 코드를 개선해나간다.

Red-Green-Refactor로 코딩하는 예시

1. Red

담긴 음료들의 가격 총합을 구하는 calculateTotalPrice 메서드를 TDD로 구현해보자.

우선 현재 calculateTotalPrice 메서드는 아래와 같이 구현이 돼있지 않은 상태이다.

    public int calculateTotalPrice() {
        return 0;
    }

이제 빨간 불이 뜨는, 즉 통과하지 않는 테스트 코드를 짜보자.

    @Test
    void calculateTotalPrice() {
        CafeKiosk cafeKiosk = new CafeKiosk();
        Americano americano = new Americano();
        Latte latte = new Latte();

        cafeKiosk.add(americano);
        cafeKiosk.add(latte);

        int totalPrice = cafeKiosk.calculateTotalPrice();

        assertThat(totalPrice).isEqualTo(8500);
    }

이 테스트를 지금 돌리면 당연히도 실패한다.

로직이 구현돼 있지 않아 항상 0을 리턴하기 때문이다.

 

2. Green

그러면 빠른 시간 내에 테스트를 통과하는 코드를 작성해보자.

“빠른 시간 내에”가 핵심이다.

    public int calculateTotalPrice() {
        return 8500;
    }

이렇게 하면 어쨌든 테스트를 통과 시킬 수 있다.

 

3. Refactor

그러면 이제 테스트 통과 상태를 유지하면서 더 좋은 코드로 고쳐나가자

    public int calculateTotalPrice() {
        int totalPrice = 0;

        for (Beverage beverage : beverages) {
            totalPrice += beverage.getPrice();
        }

        return totalPrice;
    }

이렇게 제대로 된 기능이 구현된 코드로 고칠 수 있을 것이다.

여기서 테스트 통과 상태를 유지하면서 한번 더 리팩토링을 할 수도 있다.

스트림을 통해서 코드를 더 깔끔하게 개선해보자.

    public int calculateTotalPrice() {
        return beverages.stream()
                .mapToInt(Beverage::getPrice)
                .sum();
    }

이런 red → green → refactor 사이클을 거치며 코드를 더 나은 코드로 개선해 나갈 수 있다.

 


TDD의 핵심 가치

TDD의 핵심 가치는 피드백이라고 할 수 있을 것이다.

내가 작성하는 Production Code에 대해 자주, 빠른 피드백을 받을 수 있다.

선 기능 구현, 후 테스트로 구현했을 때의 단점은 뭘까?

바로 해피 케이스에 대해서만 생각하며 구현하다가 잘못된 로직을 구현할 수 있다.

TDD로 개발하면 이러한 상황을 방지할 수 있을 것이다.

반대로 TDD로 구현을 했을 때는 어떤 이점을 볼 수 있을까?

  • 복잡도가 낮은(=유연하고 유지보수 쉬운), 테스트가 쉬운 코드를 작성할 수 있게 된다.
  • 쉽게 발견하기가 어려운 엣지 케이스를 놓치지 않고 구현할 수 있도록 해준다.
  • 클라이언트(외부 객체) 관점에서의 코드를 작성할 수 있다.