태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

달력

08

« 2017/08 »

  •  
  •  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  •  
  •  
2009.05.23 03:36

Google Test의 데이터 기반 테스트 분류없음2009.05.23 03:36

Google Test에서도 MBUnit의 RowTestNUnit의 TestCase 같은 데이터 기반 테스트라는 개념을 지원한다. Google Test에서는 이 개념을 Value-Parameterized Tests라고 부른다. 코드는 동일한데 사용하는 데이터 값 몇 개만 바뀌는 테스트 케이스가 있을 때, 코드 중복을 없애주면서도 어떤 테스트 케이스가 실패하고 성공하는지 정확하게 알려주는 유용한 기능이다.

 

그런데 아무래도 C# 보다는 유연성이 떨어지는(?) C++이라 쓰기가 마냥 쉽지만은 않아서 좀 정리를 해 둘 필요가 있다. 템플릿을 써서 구현한 기능이라 컴파일 에러라도 한 번 나면 좀 골치가 아프긴 하지만, Visual C++ 2008의 템플릿 관련 에러 메시지는 엄청나게 친절하기 때문에 미리 걱정할 필요는 없을 듯.

  1. #include "gtest/gtest.h"

    #include "gmock/gmock.h"

    #include <tuple>

     

    using testing::Return;

     

    namespace

    {

    // ParamType 구성

    // TestWithParam<T>가 받는 테스트 데이터는 T 타입의 값 하나이기 때문에, 데이터 여러 개를 넘기려고 tuple을 사용한다.

    typedef std::tr1::tuple<bool, MsoTriState, MsoAnimateByLevel, MsoTriState, MsoTriState, MsoAnimateByLevel> ParamType;

     

    // Value-Parameterized Tests를 위해서는 비어있는 Fixture라도 하나 만들어야한다.

    // TestWithParam<T>를 상속받는 것 외에는 일반적인 Test Fixture와 동일하다.

    class TestEffectDataSetEffectInformationFixture

        : public testing::TestWithParam<ParamType>

    {

    };

     

    // Value-Parameterized Tests를 위한 테스트 메서드

    // 여기서는 TEST_P 매크로를 쓴다. 일반적인 Fixture를 쓸 때에는 TEST_F 매크로를 쓰는 것과 다르다.

    TEST_P(TestEffectDataSetEffectInformationFixture, TestSetEffectInformation)

    {

        bool recheckRun;

        MsoTriState recheckRetValue;

        MsoAnimateByLevel levelEffectRetValue;

        MsoTriState animBgRetValue;

        MsoTriState animBgExpected;

        MsoAnimateByLevel levelEffectExpected;

     

        // GetParam() 함수를 써서 테스트 데이터를 얻는다.

        // 여기서는 데이터가 tuple 이라서 tie()를 써서 값을 얻는다.

        std::tr1::tie(recheckRun, recheckRetValue, levelEffectRetValue, animBgRetValue, animBgExpected, levelEffectExpected) = GetParam();

     

        MockEffectAdaptor ea;

        MockEffectInformationAdaptor eia;

     

        if (recheckRun) {

            EXPECT_CALL(ea, GetAnimateBackgroundRechecked())

                .WillOnce(Return(recheckRetValue));

        }

        else {

            EXPECT_CALL(ea, GetAnimateBackgroundRechecked())

                .Times(0);

        }

     

        EXPECT_CALL(eia, GetBuildByLevelEffect())

            .WillOnce(Return(levelEffectRetValue));

        EXPECT_CALL(eia, GetAnimateBackground())

            .WillOnce(Return(animBgRetValue));

     

        EffectData ed;

        ed.SetEffectInformation(ea, eia);

     

        ASSERT_EQ(animBgExpected, ed.AnimateBackground);

        ASSERT_EQ(levelEffectExpected, ed.BuildByLevelEffect);

    }

     

    // Value-Parameterized Tests에 테스트 데이터를 제공

    INSTANTIATE_TEST_CASE_P(Prefix, TestEffectDataSetEffectInformationFixture,

        testing::Values(

            ParamType(true, msoTrue, msoAnimateLevelNone, msoFalse, msoTrue, msoAnimateLevelNone),

            ParamType(true, msoFalse, msoAnimateLevelNone, msoFalse, msoFalse, msoAnimateLevelNone),

            ParamType(false, msoFalse, msoAnimateTextByAllLevels, msoFalse, msoFalse, msoAnimateTextByAllLevels),

            ParamType(false, msoFalse, msoAnimateLevelNone, msoTrue, msoTrue, msoAnimateLevelNone)

        ));

    } // anonymous namespace

 

테스트 실행 결과는 이런 식으로 나온다.

3>[ RUN      ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/0
3>[       OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/0
3>[ RUN      ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/1
3>[       OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/1
3>[ RUN      ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/2
3>[       OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/2
3>[ RUN      ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/3
3>[       OK ] Prefix/TestEffectDataSetEffectInformationFixture.TestSetEffectInformation/3

 

이 예제 코드에서는 테스트 데이터를 만들 때 그냥 Values()만 썼는데, Combine() 같은 걸 쓰면 주어진 값의 모든 조합을 생성해서 테스트해 준다거나 하는 기능은 그냥 코드 중복을 막는다는 차원을 넘어서는 멋진 기능이라고 생각된다.

 

그 외에 다른 사용법은 공식 문서를 보면 되는데, 실제 사용하면서 생기는 궁금증을 해결하기 위해서는 소스 코드에 주석 형태로 들어있는 사용 예제들을 보는게 좋다.

이 글은 스프링노트에서 작성되었습니다.

신고
Posted by wafe

댓글을 달아 주세요

2009.04.28 11:17

Google Test의 문자열 비교 Assertion 분류없음2009.04.28 11:17

UnitTest++과 비교했을 때 상대적으로 Google Test가 좋은 점 중에 한 가지는 문자열 비교이다. Google Test는 wide character 문자열도 잘 비교해주기 때문에 다음과 같은 문자열 비교 assertion을 써서 실제 값과 다른 경우에는 아래와 같이 문자열 값을 실패 메시지에 포함시켜서 보여준다.

  1. ASSERT_STREQ(_T("-111"), xe->GetProperty(_T("Canvas.Left")));

(노파심에 프로젝트명은 지움)

GoogleTestStringComp.png

 

Google Test의 문자열 비교 assertion에는 이런 것들이 있다.

Fatal assertion Nonfatal assertion
ASSERT_STREQ(expected_str, actual_str); EXPECT_STREQ(expected_str, actual_str);
ASSERT_STRNE(str1, str2); EXPECT_STRNE(str1, str2);
ASSERT_STRCASEEQ(expected_str, actual_str); EXPECT_STRCASEEQ(expected_str, actual_str);
ASSERT_STRCASENE(str1, str2); EXPECT_STRCASENE(str1, str2);

이 글은 스프링노트에서 작성되었습니다.

신고
Posted by wafe

댓글을 달아 주세요

2009.04.23 23:00

shared_ptr의 custom deleter 사용법 분류없음2009.04.23 23:00

내가 처음 custom deleter를 봤을 때에는, shared_ptr 생성자에서 한 번 custom deleter를 지정해주면 그 다음부터 reset()을 하더라도 항상 처음에 지정했던 deleter를 불러줄 거라는 기대가 있었다. 그래서 테스트를 해봤더니 그런게 아니네. custom deleter를 유지하고 싶다면 reset()을 할 때 deleter도 같이 넘겨줘야 한다.

 

아래 테스트 코드는 custom deleter를 지정하지 않고 reset()한 경우에는 custom deleter가 호출되지 않는다는 걸 Google Test를 이용해서 보여주는 코드이다.

 

  1. class TestSharedPtrFixture : public testing::Test
    {
    public:
       std::stringstream ss;

       TestSharedPtrFixture()
           : ss(std::stringstream::out | std::stringstream::out)
       {
       }

       void Deleter(int* p)
       {
           ss << "dtor()" << std::endl;
           delete p;
       }
    };

    // shared_ptr 기능을 제대로 알기 위한 테스트
    // shared_ptr reset() 메서드에서 dtor를 별도로 지정하는 경우 reset() 메서드를 실행할 때마다 dtor를 지정해줘야 함
    TEST_F(TestSharedPtrFixture, TestReset)
    {
       ss.str("");

       std::tr1::shared_ptr<int> sp;
       ASSERT_EQ(NULL, sp.get());

       sp.reset(new int(9), std::bind1st(std::mem_fun1(&TestSharedPtrFixture::Deleter), this));
       sp.reset(new int(8));
       sp.reset();

       ASSERT_EQ("dtor()\n", ss.str());
    }

    // shared_ptr 생성자에서 dtor를 별도로 지정하는 경우 reset() 메서드를 실행할 때마다 dtor를 지정해줘야 함
    TEST_F(TestSharedPtrFixture, TestCtor)
    {
       ss.str("");

       std::tr1::shared_ptr<int> sp(new int(9), std::bind1st(std::mem_fun1(&TestSharedPtrFixture::Deleter), this));
       ASSERT_EQ(9, *sp.get());

       sp.reset(new int(8));
       sp.reset();

       ASSERT_EQ("dtor()\n", ss.str());
    }

 

이 글은 스프링노트에서 작성되었습니다.

신고
Posted by wafe

댓글을 달아 주세요

2009.04.18 03:15

Google Test & Visual Studio 2008 분류없음2009.04.18 03:15

  1. google test를 다운받는다.(gtest-1.3.0)
  2. 필요한 폴더만 남기고 나머지는 다 지운다. 실제 프로젝트에서 쓸 때에는 include, msvc, src 폴더 외에는 필요없다.
  3. msvc 폴더의 파일 중에도, 프로젝트에서 쓸 때에는 gtest.vcproj 파일 외에는 다 필요 없으므로 지워버린다.
  4. VS2008로 gtest.vcproj 파일을 열어서 2008 포맷으로 변환해준 후 VS를 닫는다. (닫을 때 sln을 저장할 것인지 물어보는데 저장하지 않음을 선택한다.)
  5. 프로젝트를 만든다.
  6. 솔루션 폴더에 gtest-1.3.0 폴더를 통채로 옮긴다.
    1. 여러 사람이 같이 작업하는 프로젝트에서 구글 테스트 프레임워크를 쓰기에는, 솔루션 폴더에 통채로 포함시키는 편이 공유하기에 편하다.
    2. 현재 사용중인 버전을 아는 것이 좋으니까 gtest-1.3.0 같은 식으로 폴더 이름에 프레임워크 버전을 포함시켜 두도록 한다.
  7. 솔루션에 "기존 프로젝트 추가"를 해서 gtest.vcproj 를 추가한다.
  8. 솔루션에 테스트 프로젝트를 추가한다.
    1. VC++ > 일반 > 빈 프로젝트를 추가하도록 한다.
  9. 테스트 프로젝트의 참조에 gtest 프로젝트를 추가한다.
  10. gtest-1.3.0/src 폴더에 있는 gtest_main.cc 를 테스트 프로젝트 폴더로 복사하고, 테스트 프로젝트에 추가한다.
  11. 테스트 프로젝트의 "추가 포함 디렉터리"에 $(SolutionDir)\gtest-1.3.0\include 를 추가한다.
  12. 테스트 프로젝트와 gtest 프로젝의 구성 속성에서 "C/C++ > 코드 생성" 페이지의 "런타임 라이브러리"를 맞춰준다. (여기서는 다중 스레드 디버그(/MTd)로 맞췄다.)
  13. 테스트 프로젝트의 구성 속성에서 "빌드 이벤트 > 빌드 후 이벤트" 페이지의 "명령줄"에 $(TargetPath) 를 설정해준다. 이렇게 하면 프로젝트가 빌드 된 후 바로 테스트 프로젝트가 실행되고, 실행 결과가 VS 출력 창에 나타난다. gtest-output.png
  14. 실패하는 테스트 케이스가 있는 경우에는 컴파일 에러와 마찬가지로 출력 창에 소스 파일과 라인이 표시되기 때문에 마우스로 더블클릭하면 실패한 라인으로 바로 이동된다.

 

참조:

 

이 글은 스프링노트에서 작성되었습니다.

신고
Posted by wafe

댓글을 달아 주세요

  1. U.Seung 2009.04.19 17:58 신고  댓글주소  수정/삭제  댓글쓰기

    저도 요즘
    Google Test 써보고 있는데ㅋ

    C++ Unit Testing도구는 쓸때마다 n%씩 부족한 느낌이 들어서 이거.ㅎㅎ

    • wafe 2009.04.19 23:37 신고  댓글주소  수정/삭제

      언어가 C++이니 뭐 ㅎㅎ
      그래도 현재로서는 Google Test + Google Mock 조합이 최강이 아닐까 싶네. 아직 Google Mock은 문서만 보는 중이긴 하다만 ;;

  2. decoder 2009.04.20 08:48 신고  댓글주소  수정/삭제  댓글쓰기

    헐퀴 ;; 트랙백 보고 날라왔습니다~~
    윈도 프로그램 만들때 도움되겠네요. 감사합니다 :)

    • wafe 2009.04.20 14:08 신고  댓글주소  수정/삭제

      구글 테스트에 대해 검색하다가 decoder님 블로그에서 정보를 얻었거든요. 저도 뭔가 도움될까 싶어서 트랙백 날려봤습니다. ^^;

  3. 오늘도 공부... 2009.06.26 20:27 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 글 감사드립니다. 적힌 글을 따라서 진행을 하는데, 제대로 되지 않아서 글을 남깁니다.
    실제 테스트에 들어있는 CMoney(개발 프로젝트)에 대한 CMoneyTest 파일은 테스트 프로젝트의 포함을 하는가요? 아니면 개발 프로젝트에 포함을 하는가요? 저는 테스트 프로젝트에 포함을 시켰는데, 에러가 발생을 하네요. T.T 그렇다고 CMoneyTest를 개발 프로젝트에 포함을 시켜면 gtest에 대한 "포함 파일, 라이브러리"등을 포함시켜야 되는데, 그것은 좀 아니듯해서... 질문만 남겨서 죄송합니다.

    • wafe 2009.06.29 17:51 신고  댓글주소  수정/삭제

      안녕하세요 찾아주셔서 고맙습니다.

      테스트 코드가 들어가는 소스 파일들(CMoneyTest 등)은 테스트 프로젝트에 넣어야 됩니다.

      gtest에 같이 제공되는 샘플에 CMoneyTest 라는게 있나해서 찾아봤더니 그런 게 있지는 않군요. 어떤 에러인지 몰라 도움을 못 드리는게 안타깝네요.

      참고로 제가 겪었던 에러 중에 제일 흔한 것은 테스트 프로젝트와 제품 프로젝트의 '런타임 라이브러리'를 동일하게 맞추지 않은 경우였습니다.

  4. zippolook 2010.06.03 12:13 신고  댓글주소  수정/삭제  댓글쓰기

    설명 감사합니다. 저는 1.5버전으로 만들어 봤어요.
    wafe님의 글을 바탕으로 그림 추가하여 제 블로그에 포스팅 하려고 합니다. 그래도 되겠죠?

  5. mooni 2010.06.15 21:23 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요. 좋은 글 우선 감사드려요 ^^
    저도 윗분들과 똑같이 하는데 계속 LNK2019 에러가 나서요. 하루 종일 삽질을 하고 있네요.
    제가 님이 쓰신 글을 잘못이해하고 있는건 아닌지 확인 좀 해수시면 감사하겠습니다.
    gtest-1.5를 받아 세팅했구요, 솔루션 안에 A 프로젝트, ATest 프로젝트, gtest 이렇게 세개의 프로젝트가 있습니다. gtest는 글 처럼 기존 프로젝트를 추가했구요. A 프로젝트에 gtest에 있는 sample1 파일들을 넣고, ATest 프로젝트에 gtest_main.cc 와 sample1_unittest.cc 를 넣고 빌드를 하게 되면 계속 LNK2019 에러가 나네요. 글에 적힌 부분은 모두 따라했구요.
    혹시 왜 에러가 나는지 아시면 도움 좀 부탁드리겠습니다. 아,
    그리고 테스트 프로젝트의 구성 형식은 응용 프로그램(.exe)로 해놓는게 맞나요? sample1 을 모두 비우고 코드가 없이 실행할 경우 콘솔 창에 제대로 출력이 되는데 sample1 코드를 작성하면 문제가 나네요.
    음.. 장문의 글이 되었네요. ㅎ 염치없지만 혹시 아시면 도움 좀 부탁드리겠습니다.

    • wafe 2010.06.20 20:22 신고  댓글주소  수정/삭제

      블로그를 자주 확인하지 않다보니 답장이 늦었네요.

      1. 프로젝트는 EXE로 해 놓으시면 됩니다.

      2. 제가 지금 당장은 직접 돌려볼 수가 없어서 제 추측으로만 쓰게 되는 걸 좀 양해해주시면 좋겠습니다.

      링크 에러가 발생하는 건 A 프로젝트에 있는 소스 파일을 ATest 프로젝트에 추가해주지 않았기 때문이라고 생각됩니다. ATest 프로젝트에 오른쪽 클릭해서 기존파일 추가 기능을 써서 테스트 대상이 되는 cpp 파일을 추가해주면 될거라고 생각됩니다.

      그래도 링크 에러가 계속된다면 sample1을 컴파일하는데 필요한 lib 같은 게 ATest 프로젝트에는 추가되지 않았다든지 하는 문제일 것 같습니다.

  6. 손님 2010.06.22 22:34 신고  댓글주소  수정/삭제  댓글쓰기

    저도 mooni 님과 똑같은 증상이 나타나네요..
    하루종일 봐도 LNK2019 문제가 해결이 안되는데.. 혹시 원인이 뭘까요?
    1.3 버전으로 해도 증상이 같네요.. ㅜㅜ

    • 손님 2010.06.23 09:05 신고  댓글주소  수정/삭제

      해결했습니다.. ㅠㅠ
      mooni 님도 저랑 똑같이 해결하시면 될 듯 한데요..
      프로젝트가 아닌 솔루션 속성창에 가시면 project dependencies 라는 메뉴가 있습니다.
      거기서 테스트프로젝트가 구글 테스트를 depend on 하도록 설정 해주니까 링크 에러가 없어 졌네요...