TDD (Test-driven Development)

2022. 3. 25. 13:18CSE/JAVA

1. TDD란?

 

TDD는 Test-driven Development의 약자로, '테스트 주도 개발'이다.

TDD를 알아보기 전에 TDD의 Test가 무슨 뜻인지를 알아보자

 

(1) Testing이란?

Testing이란 제품 또는 서비스의 품질을 확인하는 과정으로 개발 분야에서는 소프트웨어의 버그를 찾는 과정을 말한다. 즉, 제품이 원하는대로, 예상하는대로 동작하는지 확인하는 과정이다.

이 때, 제품은 함수, 특정한 기능, Api 스펙 등을 말한다.

 

특정한 기능을 수행하는 코드가 있다면 그에 해당하는 테스트 코드를 작성한다.

그리고 해당 테스트 코드를 실행해서 모든 테스트가 패스되는지 확인을 하고, 테스트가 실패할 시, 어떤 부분이 잘못되었는지 확인하고 수정하는 과정을 거쳐야한다.

 

(2) TDD란?

위에서 이미 말한 것 처럼, TDD는 Test-driven Development의 약자로 '테스트 주도 개발'이라는 방법론이다.

이는 개발(코드 작성)전에 테스트 코드를 먼저 작성하는 방법론 중 하나이다.

 

방법은 다음과 같다.

  1. 코드 작성 전에 특정 기능에 대해서 상황을 나눈 뒤, 그 상황에 대해서 테스트를 먼저 작성하고 테스트를 실행한다.
    • 이 때, 기능을 아직 만들지 않았기 때문에, 당연히 실패하게 된다.
  2. 해당 테스트가 성공할만큼만의 코드를 작성한다
  3. 이 후, 테스트를 다시 시도한다.
    • 테스트 성공 시 : 다음 기능에 대해서 동일하게 반복한다
    • 테스트 실패 시 : 성공할 때까지, 코드를 수정해서 기능을 구현한다.

1~3번 까지는 기능을 구현하는데만 집중해서 빠르게 코드를 작성한다. 각 기능 구현 후, 필요하다면 리팩토링을 진행한다.

 

(3) TDD의 장점

TDD 방법론을 사용하게 되면 요구사항에 대한 철저한 분석과 이해를 통해 설계자의 입장에서 개발을 진행해 나갈 수 있다.

따라서, 모든 요구사항(목표)에 대해 점검할 수 있고, 사용자 입장에서 코드를 작성할 수 있다. 또한 시스템 전반적인 설계가 향상되고, 개발 집중력도 향상된다.

 

※ 해당 부분은 다음 영상을 참고하였습니다.

https://www.youtube.com/watch?v=Npi21gLIEZM 

 

2. TDD 방법론 예시

※ 해당 예시는 다음 유튜브를 따라한 코드입니다.

https://www.youtube.com/watch?v=eMU_hninZAs

(1) 기능 하나 구현하는 과정

public class ShoppingBasketTest {

    @Test
    public void totalOfEmptyBasket(){
        //ShoppingBasket class를 만들고 이를 이용해서 테스트를 하는 것이 아니라
        //테스트에 먼저 작성하고 이 후, class를 만드는 것이다
        ShoppingBasket basket = new ShoppingBasket();
        assertEquals(0.0, basket.getTotal(),0.0);
    }
    
}

위와 같이 테스트 코드를 작성하고 일단 실행해본다.

당연히 ShoppingBasket이라는 class를 작성하지 않았기 때문에 오류가 발생한다.

 

이제, 기능을 구현해보자.

public class ShoppingBasket {

    public double getTotal() {
        return 0.0;
    }
}

이 후, 테스트 코드를 다시 실행해보면, 테스트가 성공한다.

 

이러한 과정을 반복해서 다음과 같은 TDD 방법론을 통한 코드를 작성할 수 있다.

리팩토링까지 완료한 결과이다.

public class ShoppingBasketTest {

    @Test
    public void totalOfEmptyBasket(){
        //ShoppingBasket class를 만들고 이를 이용해서 테스트를 하는 것이 아니라
        //테스트에 먼저 작성하고 이 후, class를 만드는 것이다
        ShoppingBasket basket = buildBasketWithItems();
        assertEquals(0.0, basket.getTotal(),0.0);
    }

    @Test
    public void totalOfSingleBasket(){
        ShoppingBasket basket = buildBasketWithItems(new Item(100.0, 1));
        assertEquals(100.0, basket.getTotal(),0.0);

    }

    @Test
    public void totalOfTwoItems(){
        ShoppingBasket basket = buildBasketWithItems(new Item(100,1), new Item(200,1));
        assertEquals(300, basket.getTotal(),0.0);
    }

    @Test
    public void totalOfItemWithQuantityTwo(){
        ShoppingBasket basket = buildBasketWithItems(new Item(100,2));
        assertEquals(200, basket.getTotal(),0.0);
    }

    private ShoppingBasket buildBasketWithItems(Item... items) {
        return new ShoppingBasket(Arrays.asList(items));
    }
}

 

public class ShoppingBasket {
    private final List<Item> items;

    public <T> ShoppingBasket(List<Item> items) {
        this.items = items;
    }

    public double getTotal() {
        return items.stream().mapToDouble(item -> item.getSubtotal()).sum();
    }
}

 

public class Item{
    private final double unitPrice;
    private final int quantity;

    public Item(double unitPrice, int quantity) {
        this.unitPrice = unitPrice;
        this.quantity = quantity;
    }

    public double getUnitPrice() {
        return unitPrice;
    }

    public int getQuantity() {
        return quantity;
    }

    public double getSubtotal() {
        return unitPrice * (double)quantity;
    }
}