참고 : 테스트 주도 개발 시작하기 - 최범균 (가메출판사)
테스트 관련 책들을 읽었으니 TDD 를 사용해서 개발을 해보려는데... 적용하는데 어려움을 겪고 있다. 생각보다 어렵다... 아니 생각을 해서 어려운건가 싶기도 하다. 단순하게 생각할 수 있다면 TDD 적용이 보다 수월할 거 같은데, 단순하게 생각한다는 것 역시 쉬운일이 아니다...
그래서 테스트에 대해 다시 공부를 할까도 싶었지만, 현재 공부중인게 있으니 테스트 공부는 나중으로 미루고 TDD 에 대해 간단하게 복습 및 정리만 하고 넘어가려고 한다. TDD에 대해 더 알고 싶으신 분은 위 참고에 나온 책을 보는 것을 추천드립니다.
TDD(테스트 주도 개발) 이전의 개발
- 만들 기능에 대해 설계를 고민. 어떤 클래스, 인터페이스를 도출할 지 고민하고 각 타입에 어떤 메서드를 넣을지를 상상하며 시간을 들여 생각한다.
- 1번을 수행하면서 구현에 대해 고민하면서 대강 느낌?이 오면 코딩하기 시작한다
- 기능에 대한 구현을 완료한 것 같으면 기능을 테스트 한다. 이 과정에서 원하는 대로 동작하지 않거나 문제가 발생하면 과정2에서 작성한 코드를 디버깅 하면서 원인을 찾는다.
만약 테스트가 틀어지면 원인을 찾기 위한 로그 메시지를 추가하고 디버거를 이용하여 코드를 한줄 한줄 따라가면서 원인을 찾았었다. 근데 우리가 작성하는 기능은 짧을 수도 있지만 긴 경우도 있을 것이다. 그 긴 기능을 한 호흡에 작성하고 테스트를 한다? 코드가 길수록 원인을 찾는데 시간이 오래 걸릴 수 있다
TDD란?
요약 : 실패한 테스트를 통과시킨 뒤에 새로운 테스트를 추가했고 다시 그 테스트를 통과시키기 위한 코드를 작성한다. TDD는 테스트를 먼저 작성하고 테스트에 실패하면 테스트를 통과 시킬 만큼 코드를 추가하는 과정을 반복하면서 점진적으로 기능을 완성해 나가는 것이다.
TDD 는 테스트를 먼저 시작한다. 구현을 먼저 하고 테스트 하는 이전 개발 방식이 아니라, 먼저 테스트를 하고 그 다음에 구현을 한다.
테스트를 먼저 한다는 것은 기능이 올바르게 동작하는지 검증하는 테스트 코드를 작성하는 것을 의미한다. 기능을 검증하는 테스트 코드를 먼저 작성하고 테스트를 통과시키기 위해 개발을 진행한다.
먼저 기능을 검증하는 테스트를 만들어 보자
assertThat() 은 AssertJ 메서드다. AssertJ 에 대해선 따로 찾아보자.
Calculator 클래스가 없다는 컴파일 에러가 발생한다. 클래스를 생성해서 컴파일 에러를 없애자 (빨간색을 없앤다)
컴파일 에러를 없애기 전에 생각해보자. 이 코드가 어떻게 만들어진건가?
- 메서드 이름은 뭐가 좋을까? plus? sum?
- 파라미터는 몇개여야 할까?
- 정적메서드로 구현할까? 인스턴스 메서드로 구현할까?
- 메서드를 제공할 클래스 이름은 뭐가 좋을까?
이러한 고민을 하면서 만들어 진게 위의 코드다. 이제 진짜로 클래스를 생성해서 컴파일 에러를 없애자
Calculator 클래스를 생성하고 plus 메서드는 일단 0을 리턴하게끔 만들어놨다. 실행을 하면 결과는 아래와 같이 나온다.
테스트가 당연히 실패한다. 1+2의 값인 3을 기대하고 입력해놨는데 현재 plus 메서드는 0을 리턴하니 실패할 수 밖에!
이 빨강색 통과시키는 가장 쉬운 방법은 뭘까? 0을 리턴하는 plus 메서드를 3이 리턴하게끔 수정하는 것이다. 수정하면? 깔끔하게 성공한다! 여기서 그냥 파라미터 숫자 두개를 리턴하게끔 하면 되는데 왜 굳이 3을 리턴하는 코드를 만들었을까? 이에 대해서는 아래에서 설명하겠다. 하나의 케이스가 통과했으니 하나의 검증 코드를 더 넣어보자.
이러한 검증 코드(assertThat)를 추가해보자. 실행하면 ? 실패할 것이다.
검증코드에서 예상하고 있는 수는 5인데 plus 메소드의 리턴값은 3이기 때문에 테스트가 실패한다. 이제는 파라미터로 받은 두개의 값을 더하는 코드로 바꾸어 plus 메소드를 완성하자.
간단한 기능일 뿐인데 이런 과정을 겪어야 하나 싶을 수도 있겠지만...? 이런 과정을 경험한다는 것은 아래와 같은 생각을 한다는 것을 말한다.
- 어떤 케이스를 추가해 볼 수 있을까?
- 예외 케이스는 어떤게 있을까?
- 예외 케이스를 넣으면? 테스트가 실패하겠지? 어떻게 성공으로 이끌 수 있을까?
지금 이것은 두 수를 더하는 쉬워보이는 메소드다. 하지만 우리가 개발하는 기능은 이보다 복잡한 기능일 것이다. 그리고 예외사항도 더 많고 생각해내기 어려울 것이다. 하지만 개발을 하면서 이러한 과정을 거치면? 후에 버그가 생길일이 줄어들고 결과적으로는 전체적인 개발 시간 또한 단축될 것이다. 때문에 조금은 번거로울지 몰라도 처음에는 테스트 과정을 하나하나 쉬운것부터 통과시키는 연습을 진행하는게 좋다고 생각한다. 그러면 무슨 테스트 코드를 짜야 하는지 생각하는 사고력을 기를 수 있을 것 같다.
우리는 뎃셈 기능을 검증하는 테스트 코드를 만들면서 테스트 대상이 될 클래스의 이름, 메서드 이름, 파라미터 개수, 리턴 타입을 고민했다. 또한 새로운 객체를 생성할지 아니면 정적 메서드로 구현할지 등을 고민했다. 이런 고민 과정은 실제 코드를 설계하는 과정과 유사하다. 핵심은 테스트를 통과시킬만큼의 코드만 작성하는 것이다.
코드를 한번에 작성하고 개발하는게 아니라 점진적으로 개발을 하기 때문에 실패해도 찾아봐야 하는 코드의 양이 적다. 그래서 디버깅이 빠르니 개발시간도 빨라질 것이다.