FitNesse를 활용한 프로그래머 테스트 :: 2009/05/14 22:13
|
|
FitNesse라는 프레임워크가 있습니다. 이 프레임워크는 Fit을 Wiki 형태로 사용할 수 있게 해주는 좋은 도구입니다. 이 도구를 처음 접하고, 어떻게 써먹으면 좋을까 고민을 좀 했습니다. 제가 회사에서 맡은 일이 주로 네트워크 프로토콜 스택을 구현하는 일이라, 거기에 적용할 방법이 없을까 생각해봤습니다. 아니, 처음부터 그렇게 생각한 건 아닙니다. 첨엔 삽질부터 시작했죠. 그 삽질이 어떤 과정으로 진행되었는지 살펴보자면 대략 다음과 같습니다.
1.
처음에 이 프로토콜 스택을 구현하기 시작할 때, 일단 통신과 관련된 패킷 핸들링 부분은 좀 천천히 구현하고, 프로토콜의 핵심이 되는 '메시지 교환 sequence' 부터 설계하고 구현할 수 없을까 생각했습니다. 그러다 보니 결국 프로토콜 스택을 두 계층으로 나누게 되었습니다. 편의상 위에 있는 놈을 U라고 하고, 아래쪽에 있는 놈을 D라고 해 보겠습니다.
2.
먼저 저는 U를 구현했습니다. U에 대한 테스트 작성을 병행했는데, 아무래도 D를 구현하지 않았기 때문에 테스트 작성이 쉽지가 않았습니다. 제가 알고 있는 한, 이 문제를 해결하는 방법은 mocking 라이브러리를 이용하는 것이었습니다. 그래서 D를 mock 하기로 결정했습니다. 그런데 jMock을 써서 D를 mock 하려 하자 난관에 부닥쳤습니다. D를 mock 하는 건 좋은데, mock된 D가 U의 콜백 함수를 부르도록 할 방법이 마땅치 않았던 것입니다. 그래서 오픈 mocking 라이브러리들은 제쳐놓고, 제가 직접 D에 대한 mock 객체를 구현하기 시작했습니다.
3.
처음에는, D 객체는 딱 하나만 만들어 놓고, 모든 U들이 D를 통해 통신하도록 했습니다. (실제 시스템은 U+D가 장비마다 하나씩 들어가게 되어 있는데, 그것과는 다른 구성이었습니다. 나중에 문제를 일으켜 결국 리팩토링하게 됩니다. -_-;) 어찌 어찌 해서, jUnit 테스트 케이스를 완성했습니다. 테스트 케이스는 우아하게 실행되었고, 모든 테스트 케이스에 파란불이 켜졌습니다. 저는 테스트 결과와, 테스트 작성 결과로 수정되고 작성된 U의 클래스들을 다이어그램으로 정리해 보고서를 제출했습니다. 거기까지는 괜찮았고, 저는 일주일 동안 Fit의 번역 작업에만 매진할 수 있었습니다.
4.
그런데 일주일이 지나고 다시 Eclipse 앞에 앉았을 때, 저는 뭔가 문제가 있다는 사실을 깨달았습니다. U를 완성할 수 있었던 것은 좋았는데, 테스트를 위해 작성한 jUnit 프로그램이 너무 복잡했던 것입니다. 여러 개의 U 객체를 묶어 통신 시나리오를 만들고 그 통신 시나리오대로 움직이는지를 테스트하려다 보니, 결국 굉장히 길고도 이해하기 까다로운 테스트가 만들어 졌던 것이죠. 그리고 일주일이 지나자, 저는 그 테스트가 어떻게 완성된 것인지를 까먹어 버렸구요. 그 덕분에, 프로그램 수정 결과로 테스트가 깨져도 대체 테스트가 '왜' 깨진 것인지를 파악하기가 쉽지 않았습니다.
5.
결국 저는 '장문의 jUnit 테스트 케이스는 그다지 바람직하지 않다'는 교훈을 얻었습니다. 저에게는 길고 장황한 통신 시나리오를 테스트할 다른 방법이 필요했습니다.
6.
그래서 결국 FitNesse로 프로그래머 테스트를 할 방법이 없을까 생각했습니다. 일단 FitNesse를 사용하기로 결정하자, 그 방법은 어렵지 않게 찾을 수 있었습니다. DoFixture를 사용하는 것이었죠. 네트워크를 통해 발생하는 모든 행위를 DoFixture의 액션으로 구현하고, 그 액션이 올바르게 실행되는지를 보기로 결정했습니다. 그래서, 다음과 같은 테이블을 작성했습니다.
테이블 중간 중간에, 이 테스트가 의도하는 바가 무엇인지를 보여주는 텍스트를 삽입했습니다. 그리고 이 테스트를 돌리기 위해 프로그램을 수정해 나가기 시작했습니다.
7.
테이블이 테스트를 주도하기 때문에, 테스트 코드의 엔트리 포인트부터 시작되는 제어의 흐름을 따라가기가 편했습니다. 그 덕에, 테스트를 실제 테스트 대상 시스템에 돌리는 픽스쳐 코드는 비교적 시간이 흐른 뒤에 다시 보더라도 그다지 어렵지 않게 이해할 수 있었습니다.
뭔가 수정할 때 마다 테스트를 계속 돌려 봤기 때문에, 내가 어디서 실수를 저질렀는지 계속 확인할 수 있었습니다. 그리고 굉장히 심각한 리팩토링을 해야 할 때도 (나중에 D를 U 별로 하나씩 분리한 다음에 D끼리 통신하도록 만드는 수정을 했는데, 그게 이번 코딩에서는 그럭저럭 '굉장히 심각한' 리팩토링이었습니다 ㅋㅋ) 그 결과가 올바른 것이었는지 재때 확인할 수 있었습니다. 결국, 항상 다음과 같은 화면을 볼 수 있었습니다.
이 과정을 거쳐 저는 U가 가져야 하는 기능의 대부분이 정상 동작함을 확인하고, D가 어떤 식으로 동작해야 하는지에 관한 세부사항을 알 수 있었습니다. 이제 D를 개발하기 시작해야 하는데, 위의 테스트를 계속해서 활용하면 D를 구현하는 것이 그다지 어렵지는 않을 거라고 기대하고 있습니다.
FitNesse를 테스트 도구로 활용하면서, 프로그램의 테스트 가능성에 대해 좀 더 많이 생각해 보게 되었습니다. 일단 테스트 가능성에 대해 생각하기 시작하자, 프로그램의 각 모듈 (이 예의 경우에는 U와 D) 사이의 관계가 좀 더 선명하게 드러나기 시작했고, 테스트 픽스쳐의 코드도 그 관계를 좀 더 분명하게 드러내게 되었습니다. 덕분에 U와 D를 설계하면서 놓쳤던 것들이 코드에 보완되기 시작했구요.
아마 이건 UI와 테스트가 거의 같은 계층에 있기 때문일 겁니다. 테스트를 좀 더 잘 할수 있으려면 U가 외부에 제공하는 인터페이스가 명확해야 합니다. U가 테스트를 잘 지원하려면 D와의 인터페이스도 분명해야 하죠.
8.
사실 앞으로 남은 일이 더 걱정입니다만, (프로토콜 개발은 제가 직접 하는 게 아니고 용역 업체와 진행하게 되어 있습니다) 이번 개발이 잘 완료되면 FitNesse를 고객(저)과 개발자(용역업체) 사이의 소통에 활용해 생산성을 높인 사례를 확보할 수 있지 않을까... 그런 기대를 하고 있습니다. 그리고 다음번에는 어떻게 적용하면 더 좋을지도 알게 될 것 같구요.