UnitTest++과 VisualStudio 그리고 CC.Net 통합하기

제목이 뭔가 길지만. 여튼 오늘 저녁에 하려고했고, 수행한 일에 관한 것.

어제 포스팅한 것처럼 VisualStudio에서 JUnit 처럼 UnitTest를 실행해주는 애드인을 발견했는데, 이 것을 사용하려면 테스트가 들어있는 바이너리를 DLL로 바꿔줘야 한다. ((그리고 해당 DLL에서 특정 이름과 signature를 갖는 함수를 만들어주고 몇 가지를 수행해줘야 한다. 어제 포스팅을 참조 하시라)) 즉 이런 형식 ((Rica의 댓글을 보고 내가 이전 방식에 대한 설명을 안한 것을 확인하고 설명 추가))

  • 개별 테스트들을 EXE로 작성한다.
  • 개별 테스트들은 명령행 인자를 기준으로 stdout 혹은 특정 XML파일로 출력한다. (CC.Net은 XML로)
  • CC.Net은 XML 파일들을 하나로 통합(merge)해서 CI 보고서를 만들어낸다.
  • VisualStudio에서는 명령행으로 실행시켜서 stdout으로 나온 결과를 읽는다.

여기서 문제. 위에서 설명한 것처럼 CC.Net ((CruiseControl.NET .Net 프레임웍을 위한 지속통합(continuous–intergration) 툴이다.  .Net말고 일반 Windows 응용 프로그램도 약간의 손질을 통해 CI 작업이 이루어지게 할 수 있다.)) 과 통합하면서 명령행 인자 ((C/C++에서 argv, argc로 보이는…)) 에 따라서 XML 출력(CC.Net을 위해)이나 콘솔창 출력이 나오게 했다. 그런데 얘는 DLL이 아니라 실행프로그램(윈도우의 EXE 형식)이란 말이지 -_-;;

그래서 생각한 해법.

  • 모든 테스트는 DLL로 작성한다
  • 작성된 DLL을 VS 애드인(VisualUnitTest++)에서 사용한다
  • 작성된 DLL을 모두 load해서 실행하는 프로그램을 작성한다

라는 것. 그리고 CC.Net에서는 이 DLL 실행해주는 녀석의 출력을 받아서 유닛 테스트 결과를 해석하면 되게한다라는 것.

UnitTest++을 보면 테스트를 보고하는 녀석(TestReporter)를 상속받아서 모든 보고하는 기능을 하는 객체들이 구성된다 — Stdout, XML 등으로 내보내는 것들이 있다.

DLL을 만들어내는 프로젝트가 “EXTERN_C __declspec(dllexport) void RunTest( LPCSTR pSuiteName, LPCSTR pTestName, TestFailureCB CB )” 라는 것 ((VS 애드인이 저 함수를 링크해서 호출하고 각 테스트에 대한 정보 + 성공/실패 결과를 돌려받게된다.)) 만 외부에 보이는 상황인데, 외부 linkage를 하나 늘렸다. 다음처럼 생긴 녀석.

extern "C" __declspec(dllexport) int RunCommandLineTest( 
    UnitTest::TestReporter& out )
{
    return UnitTest::RunAllTests( out, UnitTest::Test::GetTestList(), 0 );
}

지정된 테스트 보고기능을 하는 객체(TestReporter&)에 테스트를 내보내게만 해주면 된다. 그리고 DLL을 로드한 후 거기에서 이 심볼을 찾아서 stdout이나 XML 출력이 되게 만들어주면 완료. 쉽지요 :p

실제로 구현해 본 테스트 실행기

입력이

테스터.exe 출력.xml test1.dll test2.dll test3.dll …

형태로 들어와야 한다.

코드는,

int _tmain(int argc, _TCHAR* argv[])
{
    if( argc == 1 )
    {
        cout << "Error: No Test-sets" << endl;
    }
    else
    {
        list<tstring> input( argv + 1, argv + argc );        
        //std::copy( input.begin(), input.end(), ostream_iterator<tstring, TCHAR>( tcout, L"\n" ) );        

        tstring out = input.front();
        input.pop_front();
        if( out.substr( out.size() - 4, tstring::npos ) != _T(".xml") )
            return -1;
        
        {// Truncate
            ofstream os( out.c_str(), ios_base::trunc );
            os.close();
        }        

        for each( tstring dll in input )
        {
            ofstream os( out.c_str(), ios_base::app );
            UnitTest::XmlTestReporter reporter( os );
            HMODULE mod = ::LoadLibrary( dll.c_str() );
            if( !mod )
            {
                cout << "Error: Can't load " << mod << endl;
                continue;
            }
            FT f = (FT)::GetProcAddress( mod, "RunCommandLineTest" );
            if( !f )
            {
                cout << "Error: Can't find tester-function in module " << mod << endl;                    
            }
            else
            {
                (*f)( reporter );
            }
            ::FreeLibrary( mod );
        }
    }

    return 0;
}

 

같이 생겼다. XML 파일로 UnitTest++의 출력을 내보내 준다. ((코드의 FT는 커맨드라인을 이용해서 실행되는 테스터용 함수(dll에 있는)의 함수형에 대한 typedef.)) Stdout으로 보내는게 필요한 경우에는 입력파일에 xml이 없거나 할 때 적당히 분기하면 될 듯. (커맨드라인에선 볼 일이 없을 것 같아서 그냥 넣지 않았음).

 

ps. 아래와 같은 이유로 삽질을 좀 하긴했다. 같은 XML Test Reporter를 재활용했더니,

<?xml version=”1.0″?>
첫번째 DLL의 출력
<?xml version=”1.0″?>
첫번째 DLL의 출력
두번째 DLL의 출력

하는 식의 초난감한 XML 파일이 만들어진 것. 원인은 아마도 XML 리포터가 하나의 XML 트리를 초기화 하지 않고 쓰거나, ofstream의 버퍼링 문제일 것 같은데. 그냥 ofstream을 초기화해주고 새 XML 리포터를 사용해서 끝냈다[…].

이제 남은 작업은 XML 헤더 선언을 1개만 남기고 지우는 것인데…그 작업하는 김에 예전에 생각했던 UnitTest 결과를 합치는 것도 해버릴까보다(…).


		
Jinuk Kim
Jinuk Kim

SW Engineer / gamer / bookworm / atheist / feminist

Articles: 935

4 Comments

  1. 이건 어떠냐:
    * 모든 테스트는 EXE로 작성한다
    * 작성된 EXE를 CC에서 바로 실행한다
    * 작성된 EXE를 모두 CreateProcess()해서 실행하는 DLL을 작성하고 VS 애드인에서 사용한다

  2. 그게 원래 구조다(…).

    VS AddIn에서 “출력”을 해석하는게 아니라 특정 link가능한 DLL 함수 하나를 특정 인자(suite 이름 / test 이름)를 가지고 실행한다. 그래서 그건 기각[…]

  3. 사실 CC.net지원을 누군가 신청하면 제가 CC.net을 안쓰기 때문에 xml출력형식같은 것들을 자문받아서 개발하려고 했는데..
    아무도 신청을 안하더군요;;;;;
    UnitTest++의 XmlReporter정도 출력이면 되는거였었나요?;;;

  4. XMLReporter 정도 출력이면 그걸 CC.Net에서 해석하는건 xsl 파일 4줄인가만 고치면되서 상대적으로 간단합니다 ~_~

Leave a Reply