회사에서 테스트 주도 개발(원제: 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++에서는 포인터 사용을 최대한 하지 않으려고 하는 경향이 있는데 그러다 보니 이런 문제가 가끔씩 생긴다 - 즉 포인터를 써야만 하는 상황이 오고야 말았을 때 시그니처가 바뀌면서 코드가 뒤집힌다.

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

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

반응형

+ Recent posts