라이브러리를 사용하는 프로젝트를 테스트할 때의 seam
Working Effectively With Legacy Code에서 말하는 seam이란 개념이 있다.
A seam is a place where you can alter behavior in your program without editing in that place.
프로그램에서 해당 지점을 수정하지 않고도 동작을 바꿀 수 있는 지점.
흔히 API가 정의되는 지점인 library 경계에서 seam을 사용하려니까 link seam이 참 편하다는걸 최근 몸으로 느꼈다. 요즘 작성하는 프로그램에서, 팀 내에서 작성한 몇 몇 라이브러리를 사용한다. 근데 이걸 test-harness 밑에서 돌리려니 mock 객체를 만들어야겠고, 그러다보니 적당한 seam을 찾아야하는데, 결국 도달하는 곳은 항상 link seam 이었다.
일단 link seam 자체를 설명하자면, 말그대로 link 시간에 이뤄지는 symbol resolution을 속여(이용해?) 테스트 프레임웍 밑에 있을 때엔 mock 객체/함수가 호출되게 하는 것. C/C++ 이라면 말 그대로 link 할 때 전달하는 object file을 바꾸면되고, Java/C# 같은 류라면 classpath나 dynamic linking을 해서 바꾸면 될 것이다. 여튼 요는,
- Library의 API를 확인하고,
- 이에 대해 똑같은 linkage를 같는 fake object(mock object; 이 문맥에서만 말하자면 mock-library 정도) 를 작성하고,
- 실제 바이너리는 실제 library binary를 링크하고,
- 테스트 프레임웍엔 mock object를 링크
하면 된다 정도?
그리고 이걸 많이 쓰게된 이유를 꼽자면, 굳이 virtual function이 아니더라도 쉽게 그 동작을 바꿀 수 있어서? 즉, C++의 모든 클래스 멤버 함수들은 기본적으로 non-virtual 이다보니 아무래도 이 link seam을 애용하게 되는듯. 아니었다면 적당한 factory + object seam을 사용했을지도 모르지만, 테스트 환경에서 동적으로 실제 라이브러리를 선택할 일도 없고하니 아무래도 link seam을 많이 쓰게 된다.
사실 지금 팀에서 쓰는 서버 프레임웍의 제일 밑단은 테스트할 때마다 링크해야하니 아예 저런 link seam을 위한 mock object를 만들어놓은 셈이지만; 여튼 비침습적인(non-intrusive) 접근 자체는 내 취향인듯(…). 그래서인지 최근 작업에 쓴 각 라이브러리(래봐야 4개?)는 다 이런 형태로 mock 을 구현해놨다.
그래서인지, 대부분의 프로젝트(Visual Studio 쪽 용어로는 solution) 구성이,
- Library A – seam 을 찾아 쪼개지 않아도 되는 것들
- Library B – seam을 찾아 쪼개야하는 것들.
- Mock library B – library B의 mock 구현
- 필요한 라이브러리들을 링크해서 사용할 프로그램 모듈
- Test-Runner
- Executable – 프로그램 모듈을 동작시키는 드라이버(?)
로 이뤄지게 되었다. 실제로 executable project 안에는 프로그램 모듈을 띄우기 위한 최소한의 정보만 들어가게 작성하게 되고, Test-runner 도 비슷하게 구성되서 프로그램 모듈을 테스트하게 작성하게 된다. 그러니까 실제로 프로그래머가 작성하는건 4~6번째?
내가 사랑해마지않는(…) python 처럼 모듈 단위에서 main 을 만들 수 있다면 좋겠지만, 그럴 순 없으니 이런 구조가 나오는건 어쩔 수 없나 싶기도하다. 하지만 다른 좋은 방법이 있다면 누가 좀 힌트라도 Orz