저는 애자일 방법론이 이야기하는 사상과 방법론들을 좋아하는 편인데(누군들...) 그중에서 TDD(Test-Driven Development)를 꽤 좋아합니다. 관련된 책도 보고 여러가지 정보들을 보게 되면서 많은 애자일 방법론 중에서 가장 실용적이면서도 개인이 실천하면서 그 이득을 거의 즉각적으로 얻을 수 있는 방법론이라고 생각하기 때문입니다. 그리고 개발자의 목표인 프로그램의 품질은 높이고 안정성을 확보할 수 있는 방법으로 제가 아는한 가장 현실적이지 않나 생각합니다.
하지만 그렇다고 딱히 TDD를 해보지 않았습니다. TDD라는 것은 물론 간단히는 테스트를 먼저 작성한다는 기본 개념을 가지고 있지만 제대로 이득을 취하려면 여러가지 관련된 것들이 필요합니다. 일반적으로 JUnit으로 대표되는 xUnit같은 도구들이 이런 것을 위한 것이고 그 외에도 CI서버라든가 리펙토링등에서 테스트를 만들었을 때의 이점을 지속적으로 느낄수 있는 도구들이 많이 있습니다. 물론 Java같은 경우 main 메서드로도 테스트를 작성할 수 있지만 핵심 로직을 테스트하기 위해서 main메서드를 사용하면 모를까 프로젝트를 TDD로 하면서 main메서드로만 한다는 것은 쉽지 않은 일일것입니다.
앞에서 말했듯이 TDD를 좋아하고 있음에도 이전에는 TDD를 할 수 있는 환경에 있지 않았기 때문에 따로 공부만 했었지 실제로 적용해 보지 못했습니다. 물론 환경에 대한 핑계를 대려는 것은 아니지만 어쨌든 그동안은 할 수 없었기에 좋아하는 방법론이라면서 경험이 없다는 건 저에게는 항상 신경쓰이는 부분이었습니다. 자주 쓰는 JavaScript에서 좀 적용해보려고 했지만 JavaScript같은 경우는 UI랑 밀접하게 물려있고 접근자체를 약간 다르게 해야했고 관련 자료도 많지 않았기 때문에 TDD경험이 많지 않은 저로써는 번번히 시도만하고 실패를 하였습니다.
어쨌든 현재 다시 Java를 하게 되었고 본격적인 업무를 수행하게 되면서 업무개발에 TDD를 적용했습니다. 사실 두려움은 좀 있었습니다. 정말 오랜만에 업무로 자바를 하는 것인데다가 업무파악도 안되어 있고 일정은 있는 상태에서 처음 사용하는 방법론을 적용해 보는 것이 과연 괜찮을까 하는 생각이었지만 TDD에 대한 어느정도의 신뢰가 있었고 TDD는 안정성과 품질을 위해서 별도로 투자해야 하는 것이 아닌 개발방법론이라고 생각하고 있었고 할수 있는 환경이 되었는데 안할 이유는 없었기에 그대로 TDD로 개발업무를 시작했습니다. 이제 한 한달반정도를 해보았고... 나중에는 또 다른 생각이 들지도 모르지만 중간 로그차원에서 남깁니다.
좋은 점
개발 속도가 느려진다는 느낌은 전혀 없었습니다. 앞에 말한대로 업무도 파악안된 상태에서 처음 적용을 해보는 것이지만 테스트를 만들기 때문에 개발하는 속도가 줄어든다는 느낌은 전혀 없었습니다. TDD를 포함하여 보통 유닛테스트를 잘 하지 않는 이유는 바쁘고 일정이 촉박해서 라는 이유가 가장 많은 것으로 알고 있는데 처음 해보는 거라 어느정도 삽질로 인해서 낭비되는 시간을 감안하고 있었음에도 훨씬 자연스럽게 진행이 되었습니다.
테스트를 먼저 작성하다 보니 무엇을 구현해야 하는지를 생각해야하다보니 개발하고자 하는 의도가 명확해 져서 프로젝트에 늦게 투입해서 전체에 대한 그림을 다 그리지 못했음에도 차근차근 이해하면서 개발할 수 있었습니다. 로직과 테스트를 동시에 생각하고 만들어내니 따로 유닛테스트를 작성하는 것보다는 훨씬 빠른 느낌이었습니다. 오히려 코드를 먼저 작성했다면 귀찮아서 유닛테스트는 작성하지 못했고 추후에 테스트할 로직을 다시 생각해야하기에 시간은 오히려 더 들었을 것입니다.
간간히 생각지 못한 타이밍에 나타나는 fail의 빨간 막대는 스트레스라기 보다는 안심을 하게 해주었습니다. 로직을 추가하고 작성하는 가운데 생각지 못한 테스트케이스가 깨지는 경우를 종종 보면서 제가 고려못한 부분을 테스트가 커버해주고 있다는 안도감을 느낄수 있었기에(완벽히 커버는 못해주겠지만) 더욱 테스트케이스를 열심히 만들수 있었고 로직변경에도 다른 부분에 대한 많은 걱정없이 작업할 수 있었습니다.
리팩토링을 무척 맘편히 할 수 있었습니다. 이전의 경험을 생각하면 간단한 코드 수정은 물론이고 구조자체를 바꿔버리는 리펙토링을 하게 되면 과연 제대로 돌아갈 것인가하는 걱정이 있었고 간단한 테스트를 해보아서 잘 돌아가서 왠지 다른데서 문제가 생길 것 같아서 더 걱정이 되기 때문에 많은 량의 리펙토링에 좀 소극적이 될 수 밖에 없었는데 전체 로직에 걸쳐서 테스트케이스가 만들어져 있다보니 (전혀는 아니지만) 훨씬 안심하고 리펙토링을 할 수 있었습니다. 구조가 맘에 안들때 좋은 구조가 생각나면 언제든지 리팩토링을 할 수 있었습니다.
Given / When / Then 템플릿은 무척이나 유용했습니다. 그냥 막 짜다가 중간부터 쓰기 시작했는데 doortts님의 추천대로 given - when - then으로 테스트를 작성하는 것은 아무런 기능은 없는 주석으로 구분해 주는 템플릿에 불과하지만 깜짝 놀랄정도로 테스트가 구조화 되고 읽기 쉬워졌습니다. 아주 좋더군요.
어려운 점
TDD를 한다고 나쁜 냄새가 잘 맡아지거나 디자인이 좋아지는 것은 아닙니다. 물론 당연한 애기지만 TDD를 한다고 이런 것들도
자동으로 얻어지지는 않았습니다. 역시 많은 경험과 지식이 필요할 것 같습니다. TDD에서 테스트가 어렵다는 것은 디자인이 좋지
않다는 징조로 볼 수 있는데 저 같은 경우는 이것이 디자인이 좋지 않은 것인지 제가 테스트케이스 작성이 미숙한 것인지 판단하기가
어려웠습니다.
테스트한다는 것은 생각보다 어려웠습니다. 처음에 시작할때는 어디서 부터 시작할지도 막막했는데 약간씩 해보다 보니 조금씩 감이 오는것 같았습니다. 아무래도 UI가 물리기 시작하면 테스트가 더 어렵기 때문에 처음에는 비즈니스로직을 작성하는 부분부터 하는 것이 좋은 것 같습니다. 어떻게 테스트해야하는 지가 꽤 어려운 부분인데 어떤 동작을 만들것인가를 생각하니까 그나마 나은 것 같습니다.
의존성이 있는 부분에 대한 테스트는 역시나 어려웠습니다. 가장 큰게 디비였는데 Mock을 연결해야 더 깔끔한 테스트를 할 수 있는데 테스트도 익숙치 않은데 Mock에 대한 부분까지 익혀서 하는 것은 시간상으로도 여유가 없었기 때문에 그냥 테스트후 트랜잭션 롤백을 하는 것으로 해서 디비를 직접 물고 테스트를 작성하였습니다.
갈수록 엉망이 되어가는 픽스쳐. 픽스쳐는 테스트를 위한 기본적 데이터인데 연습할때는 괜찮았지만 실제 업무에서 사용하다 보니 엄청나게 다양한 픽스쳐가 필요했습니다. 일반적으로 픽스쳐는 @Before에서 처리를 하는데 테스트를 만들다 보니 전체적으로 공통인 픽스쳐가 그리 많이 존재하지 않았고(제가 그렇게 구조화 하기가 어려운 것일수도) 결국 거의 모든 테스트 케이스마다 픽스처를 잡아주면서 테스트를 작성해야 했습니다.일부 공통적인 부분은 따로 메서드로 추출했지만 @Before는 시간이 지나면서 픽스처설정 용으로는 거의 쓰지 않게 되었고 픽스쳐 설정으로 인해서 테스트는 길어지고 복잡해 졌습니다.
테스트에 대한 관리는 고민을 해봐야 될 것 같습니다. 클래스에는 여러가지 메서드들이 있고 한 메서드에 대해서 어려가지 조건으로 테스트를 하다보니 시간이 지나면서 테스트 클래스에는 엄청나게 많은 테스트케이스들이 생기게 되었는데 이렇게 되다보니 어느 테스트가 어느 메서드와 관련되었는지도 찾기가 힘들었고 기능추가나 로직의 변경을 하면서 어느 부분이 테스트되고 안되었는지 정확히 파악하기가 어려웠습니다.(간단히는 커버리지툴 돌리면 되기는 하지만 논리적인 부분에서...)
리팩토링중에 테스트케이스가 만들어져야 하는 상황이 되었을때 약간 난감합니다. 예를 들어 테스트를 작성하고 로직을 작성한 후 특정 부분을 메서드 추출을 해서 private로 메서드로 만들면 보통 테스트를 작성하지 않습니다. 근데 여기서 작성을 하다보니 이 private메서드가 이 클래스가 담당해야 할 부분이 아닌 것으로 판단되어서 다른 클래스를 만들어서 위치를 이동하면서 public으로 만들어졌습니다. 그럼 여전히 이전 테스트가 옮겨진 public메서드의 테스트까지 포함하고 있지만 public메서드인데 별도의 테스트가 존재하지 않으니 뭔가 좀 찜찜합니다. doortts님의 조언으로는 public으로 나올때는 테스트를 만드는게 원칙이이라고 했는데 중복테스트를 복사해서 만들게 되는것 같아서 실용성에 대해서 고민을 해봐야 할것 같습니다.
어쨌든 처음부터 호감이 있어서 그런지는 몰라도 TDD에 생각보다 순조롭게 적응을 하고 있어서 만족하고 있고 생각하던 이점들을 실제로 느끼게 되서 더욱 맘에 들었는데 이제 TDD를 습관화 해야겠습니다. ㅎ
TDD, BDD 참 어렵습니다. ㅋ
개발시점마다 생각해보면 이번엔 꼭 TDD 할것이다라고 들어갔는데
TDD에서 작업하다가 실제 코드라인으로 넘어가서
다시 TDD 으로 넘어와야 하는데, 이 복귀가 안되더라고요. 그래서 항상 실패를 했었죠. ㅋ
최근에도 그랬고요. 제일 많이 진행된 퍼센트가 약 30% 정도 ㅋ
하지만, TDD, BDD 강추이긴 합니다. 서술하신 부분에 모두 설명이 되었네요.
30% 정도 TDD에서 진행하긴 했지만, 30% 안에서 작업되었던 Unit들의 확실함(?)은
정말 자신감이 붙더군요.
잘 읽다 갑니다. ^^
예.. 지식보다는 경험이 많이 작용을 하는듯해서 어려운 부분이 많은듯 합니다. 그래도 이제는 관련 글들이나 리소스들이 많아서 좀 나은것 같습니다.
테스트와 로직의 리듬을 타는건 저도 쉽게 안되더라구요.. 어느새 정신차리면 한쪽에 코드를 막 짜고 있게 됩니다. 그래도 결과적인 이점을 많이 주기에 좀더 익숙해져봐야죠. ㅎ
제가 "꼭!" 해야겠다고 마음 먹은게 TDD 인데, 글에 쓰신 것처럼 대체 어떻게 접근해야 할지도 모르겠고, 네트웍/DB 프로그램의 경우 Mock 을 사용해서 한다는게 잘 이해도 안됩니다. 관련 서적을 4권이나 사뒀는데(...) 아직 어떤 프로젝트에도 적용을 못하고 있습니다. 이번에 아주 큰 프로젝트를 진행하는데 과연 TDD 를 도입해도 될까요. 주요 개발 언어는 VC++ / PHP (with CodeIgniter) 가 될 것 같습니다.
Mock의 개념같은건 이해하고 있는 편이지만 한번에 너무 많은 개념을 넣으려고 하면 잘 안되는것 같아서 기본적인 부분만 해나가면서 감을 잡고 있습니다. 관련 지식은 충분히 쌓은뒤에 적용하시면 되지 않을까요?
TDD의 장점이라면 팀원 모두가 하지 않아도 혼자서도 적용할 수 있는 것이라고 생각하는데요 ㅎ 제가 VC++나 PHP는 할 줄 몰라서 어떤 도구를 이용해서 하는지는 잘 모르겠네요
저도 책만 대충 보다가 조금씩 해보니 공감이 가는군요.
그래도 어려운건 어렵네요...
해보고 블로그에 남겨주세요 ㅋㅋ
한번 시도해보려는데.....이 글읽고 많은 감도 잡게 되었구요..많은 도움이 되었습니다...
도움 되셨다니 다행이네요. ㅎㅎ 저도 처음 프로젝트에 적용해 보고 적은거였는데요. ㅎ
1.
test를 만드는 기준은 "기능"이 되어야 합니다.
보통 동사로 끝나는데요, 도서관을 예로 들면 "책을_추가한다", "책을_대여한다" 가 되겠네요.
method를 커버하려고 method에 대한 테스트를 만들다보면 서비스 기능에 대한 테스트라기보다는
라이브러리를 테스트하는 경우도 있게 되요.
테스트코드의 내용은 기본적으로는 서비스의 스펙이 되어야 맞다고 봅니다.
2.
일단 처음부터 테스트 커버리지 100%를 하는 거는 불가능합니다.
개발하며서 자연스레 누락되는 커버리지는
테스트기간, QA기간에 나오는 버그를 다시 테스트케이스로 추가하면서 자연스레 메꿔질 겁니다.
3.
private으로 만든 요소를 테스트해야 할 상황이라면 protected나 public으로 바꿔도 좋습니다.
코드를 전혀 건드리지 않고 테스트하는 것이 최고지만
그것 때문에 소중한 개발시간을 많이 투자하거나, 코드가 왕창 늘어나는 것은 별로죠.
좋은 말씀 감사합니다. 지금은 꽤 적응된 상태고 테스트가 없으면 코드를 잘 짜기가 쉽지 않아졌죠. 물론 말씀하신대로 비율이나 투자대비 효과등을 고려해야 하지만요.