회사에서 테스트 주도 개발(원제: Test Driven Development: By Example) 2판을 빌려와서 설 연휴에 보고 있다. TDD에 대해 권위있는 책이다. 처음 보는 것은 아니지만 요즘 TDD가 잘 안되서 기초를 다지기 위해 다시 한번 보고 있다.

그런데 문제 - 사실 나쁜 의미에서의 문제는 아니다 - 는 모든 코드가 Java로 짜여져 있다는 것이다. 그리고 나는 C++에서 적용하고 싶었다. 내가 이 책을 처음 보았을 때는 TDD에 대해 아무 것도 모르던 시절이라 그냥 이런 것이 있나 보다 하고 눈으로 따라가면서 읽은 것이 다였다. 원래는 키보드로 쳐가면서 했어야 했지만 그러지 못했다. 그 이유 중에 하나가 바로 C++에서 사용할 수 있는 테스팅 프레임워크를 잘 몰랐던 것이다.

지금은 GoogleTest를 몇 달 동안 사용해왔기 때문에 잘 따라갈 수 있을 것 같다.


Money 객체를 만들면서 예제를 따라가다보니 언어의 차이 때문에 몇 가지 문제가 발생했다.

첫번째는 비교 구문이다. 
Java에서는 equals()를 사용하기 때문에 operator==()로 대신 구현했다. 그런데 중간에 보면 Dollar와 Franc을 상위 클래스에서 비교하는 구문에서 RTTI를 이용하는 부분이 나온다. Java에서는 getClass()로 런타임에 객체의 타입을 알아낼 수 있다. 그래서 책에서는 통화가 다른 경우 false를 리턴하게 했다. C++에서도 typeid()를 사용할 수 있기는 한데 이상하게 제대로 동작하지 않았다. 그래서 어쩔 수 없이 해당 부분 테스트는 주석 처리해버렸다. 이것 때문에 뒤의 몇 가지 테스트는 제대로 돌려보지 못했다.

두번째는 팩토리 패턴을 구현하는 부분에서부터 발생했다.
Money 클래스에서 자식 객체인 Dollar를 리턴하는데 코드가 아래와 같았다.
Money five = Money::Dollar(5);
그런데 Money 클래스는 abstract class라서 인스턴스화가 되지 않는다는 컴파일 에러가 떠버렸다. 아악... 이 문제 때문에 잠시동안 C++ 언어를 욕하다가 그냥 shared_ptr를 사용하는 것으로 문제를 해결했다.
typedef ::std::tr1::shared_ptr<Money> MoneyPtr;
MoneyPtr five = Money::Dollar(5);
물론 거의 절반에 가까운 테스트 코드와 클래스 코드를 바꾸어야 했다. 가끔 코드를 짜다 보면 이렇게 막다른 골목에 부딪히는 경우가 있다. 나 같은 경우 C++에서는 포인터 사용을 최대한 하지 않으려고 하는 경향이 있는데 그러다 보니 이런 문제가 가끔씩 생긴다 - 즉 포인터를 써야만 하는 상황이 오고야 말았을 때 시그니처가 바뀌면서 코드가 뒤집힌다.

문제는 한 번 스마트 포인터를 사용하기 시작하면 코드 전체가 스마트 포인터를 써야 한다는 것이다. 메소드의 파라미터, 리턴값, 생성자에서 받을 포인터와 객체 내에 보관하는 포인터까지... 결국 모든 클래스의 인스턴스를 스마트 포인터로 처리했다. 

개인적으로 스마트 포인터를 쓰는 것은 나쁘지 않다고 생각하지만, 문제는 코드 전반에 스마트 포인터에 종속성이 걸리는 경우다. 후반부로 갈 수록 거의 돌이키기 힘든 디자인 문제가 되기 때문에 아주 골치가 아프다.

반응형
금요일에 회사에서 Test Driven Development에 대해 소개하는 세미나를 했다.

내가 다니는 회사에서는 한달에 한번씩 프로그래머가 세미나를 진행하는 이벤트(?)가 있다. 저번달 세미나가 끝나고 다음 세미나를 누가 할지에 대해 결정해야 하는데, 보통 서로 잘 하려고 하지 않는다.

그도 그럴 것이 세미나 준비는 상당히 귀찮은 일인데다가 시간도 들고 또 많은 사람들 앞에서 발표를 하는 것이 보통 프로그래머들의 내면 지향적인 성격과는 상당히 맞지 않아서일게다. 약간의 보상은 있긴 하다.

그런데 이번에는 왠지 내가 하고 싶었다. 그 당시에는 - 그러니까 저번 달 초 - '프로그래밍 심리학'이라는 책을 읽고 있던 때였는데, 프로그래밍과 인간의 심리와의 관계를 다룬 고전이었다. 나온지 40년이나 된 책이라서 말은 고전이라지만 그 내용은 지금 봐도 별로 어색한 감이 없었다. 오히려 프로그래머는 '프로그램을 만드는 기계'로 취급되는 지금은 현실은 40년 전에 비해 크게 나이지지 않았다는 사실을 깨닫게 해주었다. 한번 읽어보길 권한다, 정말 재미있다.

그래서 프로그래머의 심리적 요인이 개발에 얼마나 지대한 영향을 미치는지에 대해 강연하기 위해 세미나를 맡겠다고 했다. 그러면 사람들이 좀 더 자신과 남들의 마음을 이해하고, 더 즐거운 마음으로, 그리고 서로에게 상처주지 않으며 개발을 할 수 있게 될 것이라고 생각했기 때문이다.


그러고 나서 며칠 뒤 그러다가 팀원들과 식사를 하다가 세미나 내용에 대해 이야기가 나왔다. 다들 물어보길래 프로그래밍 심리학에 대해 하겠다고 하면서 이런 저런 이야기를 하다가 TDD - Test Driven Development - 를 세미나로 하면 어떨까 하는 생각을 던져봤는데 갑자기 팀원들이 그게 더 낫겠다고 입을 모아 이야기했다. 헉 이런... 프로그래밍 심리학에는 다들 별로 관심이 없는건가?

결국 대중의(?) 의견을 따라 TDD를 세미나 주제로 정했다. 사실 처음에도 프로그래밍 심리학과 TDD 중 어떤 것을 주제로 할까 고민하고 있었던 참에, 남들이 더 관심을 가지는 내용을 하는게 맞겠다고 생각했기 때문이다.


당시에는 나 또한 TDD라는 것에 관심을 가진 지 얼마 되지 않은 시점이었다. TDD 세미나 준비를 하면서 느낀 것은 남들에게 어떤 것을 가르치기 위해서는 나의 이해도와 경험이 청자보다 훨씬 뛰어나야 한다는 점이었다. 어떤 지식을 남에게 전달하는 것은 그 사람에게 내가 알고 있는 것을 이해시키고 경험을 전달하는 것인데, 나조차도 정확하게 경험하지도 이해하지도 못하면 어떻게 가르칠 수 있을까. 따라서 내가 TDD를 해보고 느끼고 배우는 것이 곧 세미나를 성공으로 이끄는 길이라고 생각했다.

처음엔 쉽지 않았다. 시작해보자는 생각은 했지만 마치 C를 처음 배우는 학생처럼 막막하기만 했다. 켄트 백의 '테스트 주도 개발'이라는 책을 읽고, 웹에서 TDD를 경험해본 사람들의 경험담을 뒤적거리기 시작했다. 특히 김창준님의 애자일 이야기 블로그에 포스팅되었던 관련된 글의 경험담이 인상적이었다. 알고 보니 김창준님은 동영상까지 찍으셨다!

박피디님의 세미나 자료도 많은 도움이 되었다.

잡설: 재미있게도 구글에서 '김창준'을 검색할 때와 네이버에서 '김창준'을 검색할 때의 결과가 아주 다르다.


C/C++에서의 TDD는 Java에 환경이 훨씬 열악한 것 같다. 그 중에서는 언어적 특성도 한 몫을 하지만, TDD 관련된 서적과 자료가 대부분 Java를 중심으로 되어 있다는 점이 그것이다. 특히 IDE의 지원도 역시 달랐는데, JUnit + Eclipse 조합으로 볼 수 있는 '빨간 막대''녹색 막대'를 Visual Studio에서는 볼 수 없다는 점이 아주아주아주!! 싫었다(현재는 극복한 상태다). Visual Studio에서는 테스트 결과를 검은 배경에 흰색 글자가 찍힌 콘솔로 봐야 했다.

나중에 찾아낸 GoogleTest콘솔로 찍어줄 때 글자색이 빨간색녹색으로 나온다는 점에서 그런 갈증을 어느 정도 해소해 준다는 점에서 정말 좋았다. 역시 구글이다는 생각이 절로 들었다.

막대 표시해주는 Add-in 찾는 데만 며칠을 썼던 것 같다. 그 중 찾아낸 것이 쑥갓님의 vutpp이다. 이것을 발견한 뒤에야 비로소 첫번째 테스팅을 작성할 수 있었다. 그만큼 Java 유저들이 막대를 보며 느끼는 재미를 나도 느껴보고 싶었던 것이다.


두번째로는 어떤 테스팅 프레임워크를 쓸까 하는 고민을 했다. 이것도 역시 며칠을 소비했다. Noel이라는 사람이 테스팅 프레임워크 6종 비교를 해둔 기사를 보고 고민 끝에 첫 테스트 코드를 CppUnitLite로 작성했다. 가장 쉽고 작으며 내 입맛대로 수정이 가능한 테스팅 프레임워크였다.

그런데 한계가 금방 왔다. 처음에는 몇 가지 테스트를 하면서 재미를 보다가, 테스트가 늘어나자 중복되는 코드가 있어 이것을 묶어주는 Fixture라고 불리는 것이 없었기 때문에 직접 구현해줘야 했다. 또 테스트를 내가 원하는 것만 실행하고 싶을 때가 있는데, 이런 기능이 없었다. 다른 프레임워크에서는 대부분 지원해주기 때문에, 또다시 CppUnitLite 보다 더 나은 테스팅 프레임워크를 찾아 떠돌았다. 그리고 아직 녹색 막대는 포기하지 않은 상황이었다.


그렇게 발견한 것이 Visual Assert였다. Visual Studio와 통합되는 전용 Add-in으로 막대를 볼 수 있었을 뿐만 아니라, Fixture도 지원하고 원하는 테스트만 실행할 수도 있었다! 그렇게 다시 행복을 맛보던 찰나... 한계가 다시 찾아왔다. 이번에는 테스트가 실패해도 다음 테스트를 그냥 진행할 수 있는 기능이 없었던 것이다. 이래가지고는 테스트가 하나만 실패해도 그 뒤쪽에서 기다리는 테스트들은 모두 실행될 수 없게 되어버린다.


결국 마지막으로 찾은 것이 GoogleTest. MS MVP이신 김용현님의 추천이 있었다. 사실 처음에 추천해주셨을 때는 '막대'를 못본다는 점 때문에 별로 신경을 쓰지 않았다. 하지만 상황이 이렇게 되자 다시 기억이 났다. "아, 이걸 한번 써보자!"

써본 결과는 대만족이었다. 딱 하나, Visual Studio에서 막대를 볼 수 없다는 것만 빼면 내가 필요한 기능이 다 들어있었다. 이미 막대는 볼 만큼 봤기 때문에 더 이상 미련을 갖지 않았다. 그리고 텍스트에 색깔이 들어있다는 점에서 어느 정도 만족했다. 이정도면 정말 만들어 준 사람들에게 고맙다는 인사라도 한번 해야 할 것 같다. Thank you!



그리고... 이윽고... 드디어, 세번째 문제가 왔다. 이 문제야 말로 정말 사람프로그래머을 환장하게 하는 문제인데, 바로 Legacy Code라는 놈이다. 현재 진행 중인 프로젝트에 적용하려고 하니 그야말로 시작부터 문제였다. 한발자국도 앞으로 나아갈수가 없었다. 레거시 코드의 종속성은 내 상상을 초월할 정도로 뿌리깊었다. 소스 코드를 몇 시간동안이나 살펴봤지만 대체 어떻게 테스트 코드를 만들어야 할 지 머릿속은 새하얗기만 했다.

단 한개의 메소드만 테스트하려고 해도, 객체를 생성하고 초기화하는 과정에서 제공해줘야 하는 다른 객체가 너무나도 많았다. 환경 설정을 읽어오는 객체는 실제로 환경 설정 파일에 접근하고 있었고, 프로세스 간 통신을 담당하는 객체는 정말로 다른 프로세스를 찾아 연결시켜주는 역할을 했다. 하지만 테스트 과정에서 진짜 환경 설정 파일과 다른 프로세스를 제공해줄 수는 없었기 때문에 테스트 코드 자체가 나올 수 없었다.

물론 Mock Object라는 것을 사용하면 이런 것도 테스트할 수 있다는 점은 이미 알고 있었지만, 문제는 경험이었다. C를 처음 접할 때 포인터라는 것을 배우는 것과 같다. 포인터를 책에서 보고, 정의를 읽고, 사용법을 보고, 예제를 보아도 책을 쓴 사람은 이 정도면 알아들었겠지? 라고 생각했을지 모르나, 배우는 입장에서는 중학생에게 미적분을 보여준 것과 비슷한 얼굴을 하게 될 것이다.

너무나도 막막한 나머지 나는 그야말로 gg를 쳤다. 망했어요~

TDD가 좋다고 알고 있고 아무리 강조해도 무슨 소용인가! 레거시 코드 앞에서 TDD를 도입한다는 것은 쉽지 않다 정도가 아니라 불가능에 가까워 보였다. 주변 사람들에게 TDD라는 걸 써보자고 했을 때 다들 보였던 반응이 그것이었다. 기존 코드에 어떻게 적용할 것이냐고.


일단은 세미나 날짜가 되어서 세미나를 진행했다. 세미나는 TDD의 '실전'이 아니라 '소개'에 중점을 두었다. 이 월례 세미나는 모든 개발자가 의무에 의해 참석하는 것이기 때문에 대부분의 사람들이 TDD에 관심이 없을 가능성이 높다는 것은 알고 있었다. 만약 TDD의 심화 과정을 다뤘다면 - 사실 지금 그정도 내공도 안되지만 - 모두들 세미나 시간에 쓰러져 잤을 것이다. 그래서 전통적인 개발의 단점은 무엇인지, TDD가 어떤 점을 해소해 줄 수 있는지, 어떤 점이 좋은지대 대해 집중적으로 설명하고, 마지막으로 아주 간단한 예제 - 자판기 - 로 시연을 진행했다.

이번 세미나로 최소 한 명 이상이 TDD에 관심을 가지고 첫번째 테스트 코드를 작성해본다면 나는 성공했다고 생각한다.


세미나는 끝났지만, 나의 깊은 고민은 아직 끝나지 않았다. 레거시 코드... 대체 어떻게 해야 할까? 리팩토링에 대한 책도, 패턴에 대한 책도 다시 한번씩 읽어봤지만 별로 도움이 되지 않는다. 이론과 실전이 이렇게나 다를 줄이야.

결국 Working Effectively with Legacy Code, 일명 WELC 원서를 주문했다. 번역판이 있긴 한데 발번역이라고 해서 차라리 원서를 보기로 결정했다. 왠만큼 MSDN을 보고 구글링을 하다 보니 이제 영어도 별로 무섭지 않다.


반응형

'일기' 카테고리의 다른 글

코딩 슬럼프  (0) 2010.11.02
프로그래머와 국회의원의 공통점  (0) 2010.11.02
레거시 코드와 씨름 중  (0) 2010.10.10
프로그래밍 심리학  (0) 2010.09.15
ONE MUST FALL 2097  (0) 2010.02.27

TDD(Test Driven Development)를 시작하고 있다. 


C++에서의 TDD의 힘겨움

그런데 유명한 TDD 책들을 보니 죄다 Java 언어로 되어 있다.
그 유명한 녹색 막대도 Eclipse IDE에 프로그래스바 플러그인이 JUnit과 함께 녹아들어가 있어서 가능했던 것이었다.

C++ 진영에서는 테스팅 프레임워크가 좀 많다. 그렇지만 IDE 애드온을 제공하는 곳은 없기 때문에 아무래도 까만 콘솔 화면밖에 볼 수 없는 것 같았다.

TDD는 안그래도 시작하기가 힘든데, 이렇게 프레임워크가 다양해서 막막했다. 대체 뭘 골라야 잘 골랐다는 소리를 들을까? Java의 JUnit과 같이 표준스러운게 있다면 선택의 여지도 좁지만 반대로 시작하기는 훨씬 쉬웠을 것이다.

노엘의 홈페이지
CppUnit
CppUnitLite
CxxUnit
UnitTest++
GoogleTest
등등...

너무 녹색 막대가 보고 싶어서(나도 'Green Bar' 보고 싶다고 ㅠㅠ) 처음에는 vutpp + CppUnitLite를 사용했다. 처음에는 녹색 막대를 본다는 사실에 많이 들떠있었지만, 버그도 좀 있고 사용하기가 생각보다 그닥 편하지가 않아서 다른 것을 찾아보게 되었다.

찾아보다 느낀 사실은 Windows + VisualStudio + C++ 조합의 개발자가 적지 않은데도 제대로 된 오픈소스 VS IDE Addon이 없다는 사실이다. TDD 책에 보면 빨간 막대 -> 녹색 막대 -> 리팩토링 이라고 말하는데 그건 자바 사정이고 C++에서 녹색 막대 보기는 물건너간것 같다.


VisualAssert의 발견

라고 생각하던 와중에 VisualAssert라는 VS Addon형 테스팅 프레임워크를 발견했다. 이거 아주 쓸만하다. 초보자라면 CppUnitLite 만큼 강추한다.

이것도 좀 쓰다 보니 문제가 있었는데, 테스트 도중 하나라도 실패하면 전체 테스트가 멈춘다는 것이다.


GoogleTest의 발견

그래서 결국 GoogleTest로 왔다.

역시 구글은 다르다는 느낌을 소스만 보고도 느꼈다. 비록 콘솔창으로 결과가 출력되지만 텍스트에 색을 입혀서 성공과 실패를 금방 알 수 있게 해준다. 녹색 막대를 못 보는 사람들을 세심한 배려(?)가 아닐 수 없다.

C++에는 리플렉션이 없어서 테스팅 프레임워크마다 하나씩 아쉬운 점이 보였는데, GoogleTest에서는 그런 점을 상당 부분 개선하려고 한 부분이 보인다.

예를 들면 특정 문자열로 시작하는 테스트만 실행시킬 수 있는 등 필터 기능과, 에러가 발생했을 때 디버거를 붙일 수 있는 기능 등 실제로 필요하다고 생각되는 기능은 다 있는 것 같다. 그리고 Windows도 차별 없이 잘 지원해주고 있다.

현재 모든 프로젝트는 GoogleTest를 사용해서 진행하고 있다.

반응형

+ Recent posts