들어가기 앞서 . . .
테스트 코드를 작성하거나, 공부를 조금이라도 했다면 들어봤을 단어 “TDD”
필자도 프로젝트를 듣고, 인턴을 하며 많이 들어봤던 단어이다. 당시에는 테스트코드에 관심이 없어 정의정도만 알고 넘어갔다. 테스트에서 TDD는 이미 너무 영향이 크기때문에 이번에 테스트 코드 강의를 들으면서 다시 정의와 왜 사용해야하는 지, 또 어떻게 적용해야하는지에 대해서 알아보도록하자.
Test Driven Development
TDD란 무엇일까?
TDD. Test Driven Development이다.
우리는 이 전의 테스트코드에서 먼저 구현부를 작성한 뒤에, 구현부에 맞춰서 테스트 코드를 작성해왔다.
TDD는 이와 반대로, 테스트 코드를 먼저 작성한 뒤, 테스트 코드에 맞춰서 구현부를 작성하는 방법론이다.
즉, 테스트가 구현과정을 주도하는 것을 말한다.
TDD의 개발주기
그렇다면 TDD는 어떤식으로 진행될까?
- RED: 실패하는 테스트 코드 작성
- 구현부를 작성하지않고 테스트 코드를 먼저 작성하니 실패하는 것은 당연하다. 우선 실패하는 테스트 코드를 작성한다.
- GREEN: 구현부를 작성하여 테스트를 통과
- 테스트에 맞춰 구현부를 작성해, 테스트를 통과하도록 로직을 작성하자. 이 과정에서는 코드의 품질이나 퀄리티는 생각하지 않고, 테스트를 통과하는 것에 초점을 맞춰 빠르게 구현부를 작성한다.
- REFACTOR: 구현 코드 개선
- 테스트를 통과하는 것을 확인하였으면, 코드의 품질을 높이기 위해 구현 코드를 리팩토링한다.
TDD는 이러한 사이클을 통해 진행된다.
근데 TDD를 왜 사용해야할까?
사실 TDD를 처음 접하게 될 시, 이걸 왜 사용해야하는 지 잘 와닿지않을 수 있다.
또 익숙하지않아 습관처럼 TDD를 적용하기가 너무너무 힘들다..
TDD의 장점을 파악하면 왜 필요한 지 자연스럽게 답을 얻을 수 있다.
TDD의 장점에 대해서 알아보자.
- 코드를 작성하기 전 설계에 대해 구체적으로 작성이 가능하다.
비즈니스 로직을 작성하기 위해, 개발자는 설계에 대해 깊게 생각하곤한다. 글로 뼈대를 잡거나, 혹은 그림으로 설계를 해나가며 비즈니스 로직을 작성하곤한다.
이러한 설계는 이후 잘못된 동작을 만들어 고치기 위한 시간을 소모하는 등의 비용을 줄이기 위해 항상 중요시되고 필요시되는 과정이다. 때문에 많은 개발자분들은 코드를 작성하기 전에 무엇을 어떻게 만들 것인지 생각하고 만들어야한다. 생각하고 만드
TDD는 이러한 설계를 강제하게 만들며, 구체적으로 동작에 대해 서술함으로써 무엇을 만들어야하는지 명확하게 판단할 수 있다. 즉, 체계적인 설계가 가능하다는 장점이 존재한다.
- 초기에 결함을 잡을 수 있도록, 오류에 대해 신속한 파악이 가능하다.
프로젝트의 규모가 커지면 커질수록 복잡성은 늘어난다.
혼자 프로젝트를 진행하거나, 소규모의 프로젝트의 경우와 달리 대규모의 프로젝트를 진행할 시, 많은 개발자가 참여하여 본인이 담당하는 부분외에 본인의 코드 수정이 다른 부분까지 영향을 미칠 수 있다.
이미 복잡성이 늘어났을 때 이에 대한 코드를 고치는 것보다, 초기에 에러를 파악하고 바로 코드를 고치는 것이 비용이 더 적게 들기 때문에 TDD를 적용하였을 때 더욱 비용이 적게든다는 장점이 있다.
- 피드백
내가 작성한 구현 코드에 대해서 빠르게 피드백을 받을 수 있다.
기존의 선 기능 후 테스트 작성의 경우, 테스트 자체의 누락 가능성이 존재하며 특정 테스트 케이스(해피 케이스)만 검증할 가능성이 있다. 테스트가 중요하다는 건 알지만, 기능 구현에 더 중점을 두는 경우가 많기 때문이다.
이렇게 테스트가 누락되면 장점의 2번에서 언급했듯이, 이후 잘못된 기능을 수정할 때의 비용이 크게 증가할 수 있다.
참고자료
TDD란 무엇이며 왜 필요한가
TDD에 대해 알아보는 것과 동시에 ‘TDD가 왜 필요한가?’ 물음에 대해 답을 찾아나가는 과정을 정리한 글이다.
jay-flow.medium.com
TDD를 적용해보자
TDD를 적용해보자.
앞서 진행했던 카페 키오스크 프로젝트를 보면, 키오스크에 전체 금액을 합산하여 반환하는 코드가 존재한다. 해당 코드를 주석처리하여 없던 기능으로 생각하고 TDD방식으로 테스트를 진행해보자.
TDD: RED단계
요구사항
CafeKiosk에 있는 calculateTotalPrice 메서드는 현재 키오스크에 담긴 모든 음료의 전체 금액을 반환해주는 메서드였다.
TDD를 적용하기 위해 주석처리해주자.
/*
public int calculateTotalPrice() {
int totalPrice = 0;
for (Beverage beverage : beverages) {
totalPrice += beverage.getPrice();
}
return totalPrice;
}*/
테스트 코드 작성
테스트 코드를 작성해주었다.
현재 calculateTotalPrice를 주석처리하여 컴파일 오류가 나기 때문에, 메서드를 생성하여 최소한의 코드로 테스트 코드를 실행시켜보자.
//TDD 적용
@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(americano.getPrice() + latte.getPrice());
}
CafeKiosk에 최소한의 메서드를 생성해주었다.
public int calculateTotalPrice() {
return 0;
}
테스트를 실행하였더니 실패하였다.
예상하는 값은 라떼와 아메리카노 값을 합한 8500원이였지만, 도출된 값은 0원이기때문에 테스트에 실패한 것이였다. 이렇게 TDD의 첫 단계인 실패하는 코드를 작성해보았다.
이제 위의 코드를 최소한의 코드로 성공하는 테스트 코드로 변경해보자.
TDD: GREEN단계
요구사항
CafeKiosk에 있는 calculateTotalPrice 메서드를 최소한의 변경으로 테스트를 통과하게 만들어보자.
그린 단계에서의 구현부는 정말 최소한의 로직으로 어떻게든 테스트만 통과하면 되게 작성하면 된다.
때문에 정말 최소한의로 이미 라떼와 아메키라노의 값을 합친 8500원을 리턴하도록 작성하였다.
public int calculateTotalPrice() {
return 8500;
}
테스트 코드는 변경점이 없기때문에 테스트를 바로 실행해보았다.
테스트를 성공적으로 수행했다.
이런식으로 GREEN 단계는 정말 어떻게든 테스트를 통과하기만 된다는 마인드로 작성해주면 된다!
이제 위의 코드를 리팩토링해보자.
TDD: REFACTOR단계
요구사항
이제 성공하는 테스트 코드를 유지하며 리팩토링을 진행해보자.
calculateTotalPrice 메서드를 현재 가지고있는 음료 리스트 반복문으로 돌아 전체 금액을 더해 반환해주는 로직으로 변경하였다.
public int calculateTotalPrice() {
int totalPrice = 0;
for (Beverage beverage : beverages) {
totalPrice += beverage.getPrice();
}
return totalPrice;
}
테스트 코드는 변경점이 없기 때문에 테스트를 바로 실행해보았다.
이번에도 테스트를 성공적으로 수행하였다.
만약에 반복문이 아닌 stream을 이용해서 전체 금액을 도출하고 싶어진다면 어떻게 해야할까?
public int calculateTotalPrice() {
return beverages.stream()
.mapToInt(Beverage::getPrice)
.sum();
}
같은 calculateTotalPrice에, 같은 동작을 하는 메서드이지만 코드는 아예 새롭게 변경되었다.
이렇게 코드를 아예 다르게 작성하여도 테스트 코드는 변경하지않고 바로 테스트를 수행할 수 있다.
테스트를 성공적으로 수행하였다.
이런식으로 과감하게 리팩토링을 수행할 수 있다.
계속 이러한 사이클을 돌면서 실패하는 테스트를 작성하고, 최소한 로직으로 테스트를 통과한 뒤에 리팩토링을 진행하면 자연스럽게 TDD를 적용하게 된다.
마무리하며
항상 TDD를 통해 테스트 코드를 작성한 뒤, 구현 코드를 작성해야한다는 말은 많이 들어왔지만, 테스트에 그닥 관심이 없었기도 하고 중요도를 몰랐다보니 한 귀로 듣고 한 귀로 흘렸던 것 같다.
이번 강의와 포스팅을 통해 왜 TDD가 필요한지에 대해 더 깊게 알 수 있었다.
말로만 듣는 것보다 확실히 이유를 알고 경험해봐야 더 크게 와닿는 것 같다!!!
습관이 되어있지않아 TDD를 적용하는게 힘들지만.. 앞으로 습관화해보도록 노력해야겠다!