TDD(Test-Driven Development)

이동욱·2023년 11월 16일
post-thumbnail

TDD란?

wikipedia에서는 TDD를 다음과 같이 정의하고 있다.

Test Driven Development (TDD) is a software development practice that focuses on creating 
unit test cases before developing the actual code. 
It is an iterative approach combining programming, unit test creation, and refactoring.

쉽게 말하면 실제 코드를 작성하기 전에 유닛 테스트 케이스를 먼저 작성하여 개발하는 방법론이다.


TDD 개발 방법

TDD 개발방법은 아래의 과정을 따른다.

  1. 테스트 케이스 작성
    구현하려는 기능에 대하여 예외 케이스들을 생각하고 이에 대한 실패하는 테스트 코드를 작성한다.

  2. 프로덕션 코드 작성
    앞서 작성한 실패하는 테스트 코드를 통과 시킬 수 있는 프로덕션 코드를 작성한다.

  3. 프로덕션 코드를 리펙토링한다.
    위 과정을 반복하며 실패, 성공에 대한 모든 테스트 케이스를 작성 한 뒤 코드를 리펙토링 한다.


예제 코드

요구사항

  1. 통화가 다른 두 금액을 더해서 주어진 환율에 맞게 변한 금액을 결과로 얻을 수 있어야 한다.
  2. 어떤 금액(주가)을 어떤 수(주식의 수)에 곱한 금액을 결과로 얻을 수 있어야 한다.
  1. 2번 요구사항 실패하는 테스트 케이스 작성
    먼저 간단해 보이는 2번에 대한 테스트 케이스를 먼저 작성 해 보겠다.
    아래 케이스를 IDE에서 작성하면 모든 부분에서 빨간줄이 그어질것이다. Dollar라는 class도 없을 뿐더러 생성자, times라는 메서드도 존재하지 않기 때문이다.
public class MoneyTest {
    public void testMultiplication() {
        Dollar five = new Dollare(5);
        five.times(2);
        assertEquals(10, fuve.amount);
    }
}
  1. 2번 요구사항 프로덕션 코드 작성
    위 테스트 케이스를 통과하기 위해 아래와 같이 프로덕션 코드를 작성한다.
public class Dollar {
    public int amount = 10;

    public Dollar(int money) {
        this.amount = amount;
    }

    public Dollar times(int multiplier) {
        return new Dollar(this.amount * multiplier);
    }

    public boolean equals(Object object) {
        Dollar dollar = (Dollar) object;
        return this.amount == dollar.amount;
    }
}
  1. 1번 요구사항 실패하는 테스트 케이스 작성
    이번에는 1번 요구사항에 대한 실패하는 테스트 케이스를 작성한다.이번에는 Franc라는 class가 없어서 에러가 날것이다.
    public void testFrancMultiplication() {
        Dollar five = new Franc(5);
        assertEquals(new Dollar(10), five.times(2));
        assertEquals(new Dollar(15), five.times(3));
    }
  1. 1번 요구사항 프로덕션 코드 작성
    위 테스트 케이스를 통과하는 Franc 프로덕션 코드는 아래와 같다.
public class Franc {
    public int amount = 10;

    public Franc(int money) {
        this.amount = amount;
    }

    public Franc times(int multiplier) {
        return new Franc(this.amount * multiplier);
    }

    public boolean equals(Object object) {
        Franc franc = (Franc) object;
        return this.amount == franc.amount;
    }
}
  1. 리펙토링
    이제 모든 테스트 케이스를 통과하는 프로덕션코드를 작성했으니 리펙토링을 진행 할 차례이다. 위 프로덕션 코드들은 아래와 같이 리펙토링 할 수 있다.
  • Dallar와 Franc는 비슷 한 속성, 메서드가 많으니 클래스의 공통 상위 클래스를 생성
  • 공통 상위 클래스인 Money 클래스를 생성해주고, 하위 클래스에서 변수를 볼 수 있도록 amount 변수를 protected로 변경하자.
  • 중복을 줄이기 위해 equals() 코드를 Money에 구현해보자

위 과정을 거쳐 최종적으로 만들어진 코드는 아래와 같다.

public class Franc extends Money{
	...
}
public class Dollar extends Money{
	...
}
public class Money{
	protected int amount;
    
    public boolean equals(Object object) {
    	Money money = (Money) object;
        return this.amount == money.amount;
    }
}

TDD의 장단점

Strong

  • TDD 방법을 사용하여 구현 시 모듈화의 수준이 높아지기에 보다 객체지향적인 코드를 만들 수 있다.
  • 에러가 발생하는 코드의 위치를 파악하기 쉽기에 추가적인 요구사항에 대하여 유연하게 대처 할 수 있다.
  • 테스트 코드를 먼저 작성하기에 개발자는 자신이 구현하려는것이 무엇인지 명확하게 인식하여 재설계 시간을 단축 시킬 수 있다.

Weak

  • 모든 로직에 대한 테스트코드를 작성하기에 생산성이 떨어진다 또한 개발자가 생각한 예외에 대하여 실패 테스트 케이스를 작성하기에 어쩔수 없이 놓치는 예외사항이 나올 수 있다.

마치며

하나의 서비스를 만들 때 분석, 설계가 80%를 차지하고 실제 코딩은 20%를 차지한다는 말을 들은 적이 있다.
구현시에도 문서를 토대로 TDD 개발방법을 따라 구현 한다면 수준 높은 객체지향적인 코드를 만들 수 있을것같다.
나는 개인적으로 모든 로직에 대해 테스트 케이스를 작성하고 실제 코드보다 테스트 코드를 먼저 작성하는 TDD에 회의적인 입장이지만 이번 토이 프로젝트에 한번 적용해 봐야겠다.

profile
Backend Developer

0개의 댓글