"코드를 작성하기 전에 테스트를 먼저 작성한다"
위 문장은 작성된 코드가 없는 데 어떻게 테스트를 진행할 수 있는 지 의구심을 만들 수 있다.
하지만 테스트 주도 개발(TDD, Test Driven Development)의 시작과 같은 말이며,
많은 개발자들이 실제 코드를 작성하기 전에 테스트를 먼저 작성하는 방식으로 개발한다.
개발자들 사이에서 TDD는 왜 떠오르게 되었을까?
이 글에서는 TDD가 무엇인지 간단하게 살펴보고 어떻게 적용되는지 확인해보도록 하자.
TDD란?
TDD는 테스트부터 시작한다.
구현을 먼저 하고 나중에 테스트하는 것이 아니라 먼저 테스트를 하고 그 다음에 구현한다.
구현된 기능이 없는데 어떻게 테스트를 진행할 수 있을까?
TDD에서 '테스트를 먼저 한다'는 것의 의미는 기능이 올바르게 동작하는지 검증하는 테스트 코드를 작성한다는 것을 의미한다. 즉, 기능을 검증하는 테스트 코드를 먼저 작성하고 테스트를 통과시키기 위한 개발을 진행하는 방법이다.
기존에는 어떻게 테스트를 진행하는가?
기존의 기능을 먼저 구현하고 테스트하는 방법은 일반적으로 다음과 같다.
1. 기능에 대한 설계를 진행 - 클래스와 인터페이스 및 타입에서 사용될 메서드
2. 설계 방향에 맞춘 구현
3. 기능에 대한 구현 완료 후 기능 테스트 진행 - 오류 발생 시 구현 내용 디버깅
이 같은 방법을 사용하면 테스트를 진행하는데 다음의 단점을 겪을 수 있다.
첫째, 과정2에서 한번에 작성한 코드가 많을 경우 디버깅 시간이 길어진다.
기능이 정상적으로 동작하지 않을 경우 원인을 찾아야하는데, 원인을 찾기 위해서 많은 코드를 탐색해야하기 때문이다.
이때, 디버깅을 위한 로그나 IDE에서 제공하는 디버거를 이용해서 코드를 한 줄씩 따라가야하는 경우가
발생할 수 있다.
둘째, 테스트 진행과정이 쉽지 않을 수 있다.
웹 애플리케이션을 개발할 경우 클래스 파일이 변경될 때 마다 톰캣의 재시작이 필요하며, 데이터의 정합성 확인을 위해 DB에 직접 접속해 SELECT 쿼리를 수행하거나 테스트 준비를 위해 미리 데이터를 INSERT 해주어야한다.
코드 작성 후 테스트를 진행할 때 기능 오류로 인해 얼마나 많은 수정과 테스트 반복을 진행해야할 지 모르며
이는 전체 개발 시간을 늘릴 수 있는 요인이 된다.
TDD는 어떤 차이가 존재하는가?
"TDD는 기능을 검증하는 테스트 코드를 먼저 작성한다."
TDD에서 기능을 구현하면서 테스트를 진행하는 과정은 다음과 같다.
1. 기능을 검증하는 테스트를 먼저 작성한다.
2. 1.에서 작성한 테스트를 통과하지 못하면 테스트를 통과할 만큼만 코드를 작성한다.
3. 테스트를 통과한 뒤에는 개선할 코드가 있으면 리팩토링한다.
4. 리팩토링 후에는 다시 테스트를 실행해서 기존 기능이 망가지지 않았는지 확인한다.
1번에서 4번의 프로세스를 반복하면서 기능을 점진적으로 완성해 나가는 과정이 전형적인 TDD의 흐름이다.
테스트 코드가 점차 추가되면서 검증하는 범위가 넒어지면서 구현도 점점 완성된다.
구현이 완료되면 리팩토링을 진행하는데, 테스트 코드 없이 기능을 우선적으로 구현한 후 리팩토링을 진행하는 행위는
잘 동작하는 코드를 건들기 때문에 심리적 불안감을 줄 수 있다.
하지만, 기능이 온전히 동작한다는 것을 검증해주는 테스트가 있으면 코드 수정에 대한 불안감을 덜어 줄 수 있다.
코드 수정에 대한 불안감을 덜어줄 뿐만 아니라 TDD는 개발 과정에서 지속적으로 코드 정리를 하므로 코드 품질이 나빠지는 것을 방지하는데 효과가 있기 때문에 유지보수 비용을 낮추는데 도움이 된다.
추가적으로 코드 수정에 대한 피드백이 빠르다. 기존 코드를 수정한 후 테스트 코드를 돌려서 수정된 코드가 올바른 지 확인하여 잘못된 코드가 배포되는 것을 방지할 수 있다.
TDD를 적용한 개발
'덧셈 기능' 이라는 간단한 기능을 통해 TDD를 살펴보자.
1. 기능을 검증하는 테스트 코드를 작성
public class CalculatorTest {
@Test
void plus() {
int result = Calculator.plus(1, 2);
assertEqual(3, result);
}
}
2. 위에서 작성한 코드의 테스트를 통과할 수 있는 코드를 작성한다.
Calculator 클래스를 생성하고 int 파라미터를 두개 받을 수 있는 static 메서드를 선언한다.
public class Calculator {
public static int plus (int a, int b) {
return 3;
}
}
3. 테스트를 통과한 후 개선할 코드가 있으면 리팩토링한다.
1번의 테스트는 통과할 수 있지만 파라미터 값이 다른 값으로 변경되고 다른 값에 대해 검증한다면 테스트 케이스 패스에 실패하기 때문에 이에 맞추어 코드를 리팩토링한다.
public class Calculator {
public static int plus (int a, int b) {
return a + b;
}
}
4. 리팩토링 후에는 다시 테스트를 실행해서 기존 기능이 망가지지 않았는지 확인한다.
3번과 같이 수정하면 두개의 int 값에 대해서 테스트 케이스를 성공할 수 있다.
마치며...
TDD 책을 읽었지만 TDD에 대한 감을 잡지는 못했다.
하지만 지난 겨울 진행했던 항해 플러스를 통해 개발에서 TDD를 사용하는 이유를 체감할 수 있었고
TDD가 무엇인지 맛을 보았다.
앞으로 효율적인 개발을 위해 TDD를 좀더 활용하는 개발자가 될 수 있도록 도전해보자.
References
- 테스트 주도 개발 시작하기 - 최범균