Skip to content


UnitTest 프레임웍을 슬슬 바꿀 시점인가?

회사에서 C++로 작성하는 프로그램의 이런저런 유닛테스팅 용도로 UnitTest++을 사용해왔다. 굉장히 간결한 라이브러리이고 (header file 몇 개 + 라이브러리 하나), 전체 테스트 스윗이나 테스트 케이스 설정도 다음과 같은 형태라 무척 간단한다.

SUITE( SomeSuite )
{
    TEST( Test1 )
    {
          CHECK( … );
     }

    …
}

그래서 이 UnitTest++을 꽤나 편하게 쓰고 있었는데 굉장히 맘에 안드는 점이 몇 가지 있다.

테스트 케이스를 선별적으로 실행하기 힘들다 / 테스트 실행 인터페이스가 기본적으론 제공되지 않는다

기본적으로 제공되는 외부 인터페이스가 1갠데, 테스트를 실행하는 함수인 UnitTest::RunAllTest() 함수가 그 것이고 다음과 같은 인자를 받는다.

  • TestReporter : console 출력할지 특정 형태(XML이나 텍스트…)로 출력할지 결정함
  • Test-case들의 목록
  • 실행할 suite 이름
  • 실행할 test-case 이름

을 인자로 받고, 대부분의 경우 NULL이어도 된다. 

일부만 실행하려면 suite을 주거나 test-case 이름을 줘야하는데, 이러면 테스트가 전체적으로 잘 짜여있는 경우는 쉬운데 그렇지 않은 경우엔 참 뭣 같다(…). 게다가 저걸 커맨드라인이나 TestRunner 류에서 어떻게 전달할지도 따로 구현되어있질 않다.

내 경우엔 boost::program_options를 써서 *nix 스러운 커맨드라인 해석1 을 하게 했는데, 일단 짜서 유지해야하는게 좀 귀찮더라. 라이브러리 말고 딴걸 추가로 들고다녀야 된다는 점에서 -_-

VisualUnitTest++ 이라고  VisualStudio 2005/2008과 통합되는 TestRunner가 있다는게 다행이지만, 커맨드라인 인터페이스가 없다는 점은 정말 감점 요인(…).

근데 최근에 다른 UnitTest 프레임웍을 살펴보다가 google test를 써보게 되었는데 이 녀석은 기본적으로 커맨드 라인 파서가 포함되어 있다. 게다가 환경변수로 지정하는 것도 가능해서 수고가 많이 줄어든다. 덤으로 미운놈은 뭘 해도 밉다고 UnitTest++의 XML 출력은 사람이 읽으란게 아니다(한줄로 나오냐 orz)

 

테스트 케이스의 흐름(control-flow)를 조절하기가 힘들다

덤으로 테스트 케이스 쓸 때 제일 짜증나는게, assert를 함부로 쓸 순 없으니(유닛 테스트 자체가 멈추니까), 검사는 하되 그 검사가 실패하면 해당 테스트 케이스 전체를 실패로 처리하는 기능이 자주 필요했는데, 그런게 없다. 

UnitTest++에는 값 검사 매크로가 CHECK(), CHECK_EQUAL() 등등이 있는데, 값 검사가 실패(테스트 케이스가 잘못된)인 경우에도 무조건 실행된다. 이건 사실 당연한거지만, 특정한 경우 — 예를 들어 특정 파일을 여는데 이 파일이 없어서 그 이하의 테스트가 무의미한 경우 등등 — 에는 이게 좀 짜증난다. 테스트 케이스에서 내가 예외 처리도 하고 있어야 겠니? Orz

google test의 경우 값 검사 매크로가 ASSERT_* 계열과 EXPECT_* 계열로 나뉜다. ASSERT_* 류의 경우 해당 값 검사가 실패하면 그 테스트 케이스를 실패로 간주하고 다음 테스트 케이스로 넘어간다. EXPECT_* 류는 UnitTest++의 동작(실패하건 말건 다음 값 검사로 넘어감)과 같다.

즉, 어떤 파일을 연다고 치자. ASSERT_TRUE( some_fstream.good() ); 을 쓴다면 파일을 못 열면 해당 테스트 케이스는 실패다. 그리고 그 테스트의 다른 검사는 실행되지 않는다. 반대로 EXPECT_TRUE( some_fstream.good() ) 이라면 파일이 안 열려도 테스트는 속행하겠단 얘기다… UnitTest++ 에선 이런 실행 흐름의 조절이 불가능하고, 이는 일종의 스트레스 — 특히 저런 테스트 이후에 뭔가 실행되면 assertion-failure가 일어나는 경우 등등 — 로 다가올 뿐.

CruiseControl 과 통합하는 문제도 별 어려움이 없을듯하니, 이대로면 조만간 UnitTest++ 을 버리고 google test로 넘어가지 않을까 한다. 결정적으로 문서화의 질/양도 후자가 앞섬에야2

ps. gtest는 페도라 등 주류 *nix 계열 OS에 팩키지로 제공되기도 하고(…), 덤으로 google mock framework이란 훌륭한 물건도 있다.

  1. –help 가 들어오면 help-message를, –ouput or -o 가 들어오면 파일 출력의 이름을 받게 했다. 비슷하게 –suite/-s 로 스윗 이름을, –testcase/-t로 테스트 케이스를 받는다. getopt 류의 해석을 해주는 boost 라이브러리가 있다는 점에 ㄳ []
  2. UnitTest++이 매우 단순한 라이브러리인 탓도 있는데, 단 한 장의 문서로 모든걸 설명하고 있다. google test의 경우 기본문서/상세문서 두 가지가 꽤 자세한 설명/예와 합쳐져 있다. []
이 저작물은 별도로 명시하지 않은 경우, Creative Commons Attribution-Share Alike 3.0 Unported License에 따라 이용하실 수 있습니다.

Posted in Computer.

Tagged with , , .


4 Responses

Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.

  1. Rica says

    팀에서 쓰고 있는 디버그 관련 라이브러리가, ASSERT에 콜백을 등록해서 실행 단위마다 ASSERT 실패 동작을 다르게 할 수 있게 되어 있다. (M모 게임에서 물려받은 유산이지)

    테스트 프로젝트에서는 ASSERT(false) 걸리면 throw 하게 하니 UnitTest++도 쓸만했음 (…)

  2. rein says

    아니 그러면 되긴하는데 (실제로 그런 류의 매크로를 만들기도 하고), 왠지 이런식으로 계속 고치는게 좀 피곤해서 -_-;;

    그냥 잘 만들어진 라이브러리도 있는데 내가 고치려니 억울하더라고(?)

  3. 감성코드 says

    적절한 Cpp용 UnitTest를 찾고 있는 중인데 구글 테스트를 한 번 써봐야 겠군요. 처음 접해서 그런 건지, Cpp용 Unit이 원래 그런건지 다른 모듈들에 비해 추가적인 작업을 해줘야 하는 것들이 많네요(혹은 Visual Studio를 써서 그런건지)

  4. rein says

    감성코드 / 한 줄 요약하자면 Reflection이 없어서(…) 좀 그렇죠.

    Java/C# 혹은 python 등등의 언어에선 해당 클래스에 무슨 메서드가 있는지 runtime에 확인할 수 있지만 C++에선 그런게 *적어도 쉽게는* 안됩니다. (Java나 C#이면 특정 클래스를 던져주고 거기에 있는 test??? 메서드를 다 실행한다! 하는 식이 쉽지만 C++에선 그렇게 하긴 힘드니)

    그래서 (일단 코드상으론 바로 보이지 않는) 각종 static 변수를 동원하거나, 매크로를 써서 자동으로 등록되게 해놓거나 해서 그렇습니다.



Some HTML is OK

or, reply to this post via trackback.