메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

오픈 소스 C/C++ 유닛 테스트 도구, Part 1: Boost 유닛 테스트 프레임워크 알아보기

Arpan Sen, Technical lead, Synapti Computer Aided Design Pvt Ltd
Arpan Sen은 전자 설계 자동화 산업에서 소프트웨어 개발 분야에 근무하는 선임 엔지니어이다. 여러해 동안 Solaris, SunOS, HP-UX 및 IRIX를 포함한 UNIX는 물론 Linux와 Microsoft Windows의 여러 제품과 관련된 업무를 맡아 왔다. 소프트웨어 성능 최적화 기법, 그래프 이론 및 병렬 컴퓨팅에 많은 관심을 가지고 있다. Arpan은 소프트웨어 시스템 분야에서 대학원 과정을 마쳤다. 이메일 주소는 arpansen@gmail.com이다.

요약:  모든 소프트웨어 제품에 회귀 스위트가 필요하다는 것은 말할 필요도 없습니다. 일반적으로 유닛 테스트 프레임워크는 필요할 때마다 테스트 팀에 의해 개발되어 왔습니다. 이 방식을 사용하면 테스트 스위트의 유지보수가 힘들어지고 시간/메모리 성능에 대한 프로그램 실행 모니터링과 같은 작업을 운영 체제 사이에서 이동할 수 없게 됩니다. 이러한 사항을 고려하여 이 일련의 기사에서는 오픈 소스 소프트웨어를 사용한 복잡한 회귀 프레임워크 작성을 위해 선택할 수 있는 사항에 대해 소개합니다. 시리즈의 Part 1인 이 기사에서는 C/C++ 기반 제품의 Boost 유닛 테스트 프레임워크에 대해 설명합니다.

이 연재 자세히 보기

원문 게재일:  2009 년 12 월 08 일 번역 게재일:   2010 년 3 월 16 일
난이도:  중급 영어로:  보기 PDF:  A4 and Letter (60KB | 16 pages)Get Adobe® Reader®
페이지뷰:  3913 회
의견:  


유닛 테스트란?

복잡한 C/C++ 코드에는 버그가 있을 가능성이 매우 높으며 코드 작성 후 이러한 버그를 테스트하는 것은 건초더미에서 바늘을 찾는 것과 마찬가지이다. 코드 작성 시 특정 영역(예: 집중적인 계산이 필요한 일부 C 함수 또는 큐와 같은 특정 데이터 구조를 모델링하도록 요구하는 일부 C++ 클래스)을 목표로 하는 소규모 (유닛) 테스트를 추가하여 개별 코드 조각을 테스트하는 것이 더 현명한 방법이다. 이렇게 하면 이 철학에 따라 빌드된 회귀 스위트에는 유닛 테스트 콜렉션과 테스트를 실행하고 결과를 보고하는 테스트 드라이버 프로그램이 포함되어 있다.


특정 함수 또는 클래스에 대한 테스트 생성

텍스트 편집기와 같은 복잡한 코드의 경우 외부 테스터는 특정 루틴을 목표로 하는 테스트를 생성할 수 없다(외부 테스터는 내부 코드 조직에 대한 아이디어가 많지 않음). Boost가 유용한 곳은 화이트 박스 테스트이다. 개발자인 사용자는 클래스 및 함수에 대한 시맨틱 검사를 수행하는 테스트를 코딩한다. 코드의 향후 유지보수자가 특정 지점에서 원래 논리를 변경하고 위반이 발생하는 순간 유닛 테스트는 실패하기 때문에 이 프로세스가 가장 중요하다. 화이트 박스 테스트를 사용하면 디버거를 사용하지 않아도 잘못된 부분을 쉽게 확인할 수 있는 경우가 자주 있다.

Listing 1에 있는 단순 문자열 클래스를 생각해 보자. 이 클래스는 강력한 클래스가 아니므로 Boost를 사용하여 테스트한다.


Listing 1. 창의적이지 않은 문자열 클래스
	
#ifndef _MYSTRING
#define _MYSTRING

class mystring { 
  char* buffer; 
  int length;
  public: 
    void setbuffer(char* s) { buffer = s; length = strlen(s); } 
    char& operator[ ] (const int index) { return buffer[index]; }
    int size( ) { return length; }
 }; 

#endif

일반적인 문자열 관련 검사 중 일부는 비어 있는 문자열의 길이가 0인지 유효성 검증하여 오류 메시지나 예외 등에서 인덱스를 벗어난 결과에 액세스한다. Listing 2에는 문자열 구현을 위해 작성할 만한 테스트 중 일부가 표시된다. Listing 2에 있는 소스를 실행하려면 g++(또는 기타 표준 준수 C++ 컴파일러)로 컴파일하기만 하면 된다. 별도의 기본 함수는 필요하지 않으며 코드에서 링크 라이브러리를 사용하지도 않는다. Boost 설치의 일부인 unit_test.hpp 헤더에는 필요한 모든 정의가 포함되어 있다.


Listing 2. 문자열 클래스에 대한 유닛 테스트
	
#define BOOST_TEST_MODULE stringtest
#include <boost/test/included/unit_test.hpp>
#include "./str.h"

BOOST_AUTO_TEST_SUITE (stringtest) // name of the test suite is stringtest

BOOST_AUTO_TEST_CASE (test1)
{
  mystring s;
  BOOST_CHECK(s.size() == 0);
}

BOOST_AUTO_TEST_CASE (test2)
{
  mystring s;
  s.setbuffer("hello world");
  BOOST_REQUIRE_EQUAL ('h', s[0]); // basic test 
}

BOOST_AUTO_TEST_SUITE_END( )

BOOST_AUTO_TEST_SUITEBOOST_AUTO_TEST_SUITE_END 매크로는 각각 테스트 스위트의 시작과 끝을 표시한다. 개별 테스트는 이러한 매크로 사이에 위치하기 때문에 시맨틱은 C++ 네임스페이스와 비슷하다. 각각의 개별 유닛 테스트는 BOOST_AUTO_TEST_CASE 매크로를 사용하여 정의된다. Listing 3에는 Listing 2에 있는 코드의 출력이 표시된다.


Listing 3. Listing 2에 있는 코드의 출력
	
[arpan@tintin] ./a.out
Running 2 test cases...
test.cpp(10): error in "test1": check s.size() == 0 failed

*** 1 failure detected in test suite "stringtest"

이전 Listing에서의 유닛 테스트 작성에 대해 자세히 살펴보자. 기본적인 아이디어는 Boost 제공 매크로를 사용하여 개별 클래스 기능을 테스트하는 것이다. BOOST_CHECKBOOST_REQUIRE_EQUAL은 코드 출력을 유효성 검증하기 위해 Boost에서 제공하는 사전 정의된 매크로(테스트 도구로도 알려져 있음) 중 일부이다.


Boost 테스트 도구

Boost에는 기본적으로 표현식을 유효성 검증하는 데 사용되는 매크로인 테스트 도구의 전체 호스트가 포함되어 있다. 테스트 도구의 세 가지 기본 카테고리는 BOOST_WARN, BOOST_CHECKBOOST_REQUIRE이다. BOOST_CHECKBOOST_REQUIRE의 차이점은 전자는 가정이 실패한 경우에도 테스트가 계속 진행되는 반면 후자는 가정이 실패한 경우 심각한 오류로 판단되어 테스트가 중지된다는 것이다. Listing 4에서는 평범한 C++ 코드를 사용하여 이러한 도구 범주 사이의 차이점을 인식시킨다.


Listing 4. Boost 테스트 도구의 세 가지 변형 사용하기
	
#define BOOST_TEST_MODULE enumtest
#include <boost/test/included/unit_test.hpp>

BOOST_AUTO_TEST_SUITE (enum-test) 

BOOST_AUTO_TEST_CASE (test1)
{
  typedef enum {red = 8, blue, green = 1, yellow, black } color;
  color c = green;
  BOOST_WARN(sizeof(green) > sizeof(char));
  BOOST_CHECK(c == 2); 
  BOOST_REQUIRE(yellow > red); 
  BOOST_CHECK(black != 4);
}

BOOST_AUTO_TEST_SUITE_END( )

첫 번째 BOOST_CHECK가 실패하기 때문에 첫 번째 BOOST_REQUIRE도 실패한다. 하지만 BOOST_REQUIRE가 실패하면 코드가 종료되기 때문에 두 번째 BOOST_CHECK에 도달하지 않는다. Listing 5Listing 4 코드의 출력이 표시된다.


Listing 5. BOOST_REQUIRE와 BOOST_CHECK 사이의 차이점 이해하기
	
[arpan@tintin] ./a.out
Running 1 test case...
e2.cpp(11): error in "test1": check c == 2 failed
e2.cpp(12): fatal error in "test1": critical check yellow > red failed

*** 2 failures detected in test suite "enumtest"

비슷한 행에서 극한 상황에 대해 일부 개별 함수 또는 클래스 메소드를 검사해야 하는 경우 가장 쉬운 방법은 새 테스트를 작성한 후 인수 및 예상 값을 사용하여 해당 루틴을 호출하는 것이다. Listing 6에 예제가 제공된다.


Listing 6. Boost 테스트를 사용하여 함수 및 클래스 검사하기
	
BOOST_AUTO_TEST(functionTest1) 
{
  BOOST_REQUIRE(myfunc1(99, ‘A’, 6.2) == 12);
  myClass o1(“hello world!\n”);
  BOOST_REQUIRE(o1.memoryNeeded( ) < 16);
}

패턴 일치

일부 함수에서 생성된 출력을 "골든 로그"에 대해 테스트하는 것이 일반적이다. 여기서도 BOOST_CHECK가 유용하며 Boost 라이브러리의 output_test_stream 클래스도 사용해야 한다. output_test_stream은 골든 로그 파일(아래 예제의 run.log)로 초기화된다. C/C++ 함수의 출력이 이 output_test_stream 오브젝트에 피드된 후 이 오브젝트의 match_pattern 루틴이 호출된다. Listing 7에 세부 사항이 제공된다.


Listing 7. 골든 로그 파일에 대한 패턴 일치
	
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <boost/test/output_test_stream.hpp>
using boost::test_tools::output_test_stream;

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
    output_test_stream output( "run.log", true );
    output << predefined_user_func( );
    BOOST_CHECK( output.match_pattern() );
}

BOOST_AUTO_TEST_SUITE_END( )

부동 소수점 비교

회기 설정에서 가장 까다로운 검사 중 하나는 부동 소수점을 비교하는 것이다. Listing 8에 있는 코드를 보면 겉으로 보기에는 아무 문제가 없는 것처럼 보인다.


Listing 8. 작동하지 않는 부동 소수점 비교
	
#define BOOST_TEST_MODULE floatingTest
#include <boost/test/included/unit_test.hpp>
#include <cmath>

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
  float f1 = 567.0102;
  float result = sqrt(f1); // this could be my_sqrt; faster implementation
                                      // for some specific DSP like hardware
  BOOST_CHECK(f1 == result * result);  
}

BOOST_AUTO_TEST_SUITE_END( )

이 테스트 실행 시 BOOST_CHECK 매크로는 표준 라이브러리의 일부로 제공된 sqrt 함수를 사용함에도 불구하고 실패한다. 그렇다면 무엇이 잘못되었을까? 부동 소수점 비교에서 발생하는 문제는 정밀도 문제이다. f1result*result는 몇 개의 소수점 자리 이후부터 달라지기 시작한다. 이 상황을 해결하기 위해 Boost 테스트 유틸리티는 BOOST_WARN_CLOSE_FRACTION, BOOST_CHECK_CLOSE_FRACTIONBOOST_REQUIRE_CLOSE_FRACTION 매크로를 제공한다. 이 세 가지 매크로 중 어느 것이라도 사용하려면 사전 정의된 Boost 헤더 floating_point_comparison.hpp를 포함해야 한다. 세 가지 매크로의 구문은 모두 동일하기 때문에 이 기사에서는 검사 변형(Listing 9 참조)에 대해서만 다룬다.


Listing 9. BOOST_CHECK_CLOSE_FRACTION 매크로의 구문
	
BOOST_CHECK_CLOSE_FRACTION (left-value, right-value, tolerance-limit);

Listing 9에 있는 BOOST_CHECK를 사용하는 대신 허용 한계가 0.0001인 BOOST_CHECK_CLOSE_FRACTION을 사용한다. Listing 10에서는 이제 코드가 어떻게 표시되는지 보여준다.


Listing 10. 작동하는 부동 소수점 비교
	
#define BOOST_TEST_MODULE floatingTest
#include <boost/test/included/unit_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <cmath>

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
  float f1 = 567.01012;
  float result = sqrt(f1); // this could be my_sqrt; faster implementation
                                      // for some specific DSP like hardware
  BOOST_CHECK_CLOSE_FRACTION (f1, result * result, 0.0001);  
}

BOOST_AUTO_TEST_SUITE_END( )

이 코드는 정상적으로 실행된다. 이제 허용 한계Listing 10에 있는 허용 한계를 0.0000001로 바꾼다. Listing 11에 출력이 표시된다.


Listing 11. 승인할 수 없는 허용 한계로 인해 비교가 실패한 예제
	
[arpan@tintin] ./a.out
Running 1 test case...
sq.cpp(18): error in "test": difference between f1{567.010132} and 
    result * result{567.010193} exceeds 1e-07

*** 1 failure detected in test suite "floatingTest"

프로덕션 소프트웨어에서 계속 반복되는 또다른 일반적인 잘못된 문제점은 double 유형과 float 유형의 변수 비교이다. BOOST_CHECK_CLOSE_FRACTION의 장점은 이러한 비교를 수행하지 않아도 된다는 것이다. 매크로의 왼쪽 값과 오른쪽 값의 유형은 동일해야 한다(float 또는 double). Listing 12에서는 f1이 double이고 result가 float이면 컴파일 중에 오류가 표시된다.


Listing 12. 오류: BOOST_CHECK_CLOSE_FRACTION에 대한 왼쪽 인수와 오른쪽 인수의 유형이 다름
	
[arpan@tintin] g++ sq.cpp -I/u/c/lib/boost
/u/c/lib/boost/boost/test/test_tools.hpp: 
   In function 
       `bool boost::test_tools::tt_detail::check_frwd(Pred, 
       const boost::unit_test::lazy_ostream&, 
   boost::test_tools::const_string, size_t, 
   boost::test_tools::tt_detail::tool_level, 
   boost::test_tools::tt_detail::check_type, 
   const Arg0&, const char*, 
   const Arg1&, const char*, const Arg2&, const char*) 
   [with Pred = boost::test_tools::check_is_close_t, Arg0 = double, 
   Arg1 = float, Arg2 = boost::test_tools::fraction_tolerance_t<double>]':
sq.cpp:18:   instantiated from here
/u/c/lib/boost/boost/test/test_tools.hpp:523: error: no match for call to
 `(boost::test_tools::check_is_close_t) (const double&, const float&, 
     const boost::test_tools::fraction_tolerance_t<double>&)'

사용자 정의 조건부 지원

Boost 테스트 도구는 부울 조건을 유효성 검증한다. 테스트 도구를 보강하여 더 복잡한 검사를 지원할 수 있다(예: 두 목록의 컨텐츠가 동일한지 여부나 특정 조건이 벡터의 모든 요소에서 유효한지 여부 판별). 또한 BOOST_CHECK 매크로를 확장하여 사용자 정의 조건부 지원을 수행할 수 있다. 사용자 정의 C 함수에서 생성된 목록의 컨텐츠에 대해 사용자 정의 검사를 수행해보자. 결과에 1보다 큰 요소가 모두 포함되어 있는지 검사한다. 사용자 정의 검사 함수는 boost::test_tools::predicate_result 유형을 리턴해야 한다. Listing 13에 세부 사항이 표시된다.


Listing 13. Boost 테스트 도구를 사용하여 복합 조건부 유효성 검증하기
	
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>

boost::test_tools::predicate_result validate_list(std::list<int>& L1)
{ 
  std::list<int>::iterator it1 = L1.begin( );
  for (; it1 != L1.end( ); ++it1) 
   { 
     if (*it1 <= 1) return false; 
   }
  return true;
}

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
    std::list<int>& list1 = user_defined_func( );
    BOOST_CHECK( validate_list(list1) );
}

BOOST_AUTO_TEST_SUITE_END( )

predicate_result 오브젝트에는 validate_list의 예상 리턴 유형과 실제 리턴 유형이 다르더라도 코드가 정상적으로 작동하는 이유에 대해 설명하는 부울 값을 승인하는 내재적 생성자가 포함되어 있다.

Boost를 사용하여 복합 조건부를 테스트하는 데 사용할 수 있는 다른 방법인 BOOST_CHECK_PREDICATE 매크로도 있다. 이 매크로를 사용하는 경우 장점은 predicate_result를 사용하지 않는다는 것이다. 하지만 구문은 그다지 매끄럽지 않다. 사용자는 함수 이름과 인수를 BOOST_CHECK_PREDICATE 매크로에 전달해야 한다. Listing 14는 다른 매크로를 사용한다는 점을 제외하면 Listing 13과 기능이 동일하다. 이제 validate_result의 리턴 유형은 부울이다.


Listing 14. BOOST_CHECK_PREDICATE 매크로
	
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>

bool validate_list(std::list<int>& L1)
{ 
  std::list<int>::iterator it1 = L1.begin( );
  for (; it1 != L1.end( ); ++it1) 
   { 
     if (*it1 <= 1) return false; 
   }
  return true;
}

BOOST_AUTO_TEST_SUITE ( test ) 

BOOST_AUTO_TEST_CASE( test )
{
    std::list<int>& list1 = user_defined_func( );
    BOOST_CHECK_PREDICATE( validate_list, list1 );
}

BOOST_AUTO_TEST_SUITE_END( )


파일에 여러 테스트 스위트 포함하기

단일 파일에 여러 테스트 스위트를 포함할 수 있다. 각각의 테스트 스위트에는 파일 내에 정의된 BOOST_AUTO_TEST_SUITE... BOOST_AUTO_TEST_SUITE_END 매크로 쌍이 있어야 한다. Listing 15에는 동일한 파일 내에 정의된 두 가지 서로 다른 테스트 스위트가 표시된다. 회귀 실행 시 사전 정의된 –log_level=test_suite 옵션으로 실행 파일을 실행한다. Listing 16에서 알 수 있듯이 이 옵션을 사용하여 생성된 출력은 훨씬 더 상세하며 신속하게 디버깅할 수 있다.


Listing 15. 단일 파일에 여러 테스트 스위트 사용하기
	
#define BOOST_TEST_MODULE Regression
#include <boost/test/included/unit_test.hpp>

typedef struct {
                 int c;
                 char d;
                 double e;
                 bool f;
               } Node;

typedef union  {
                 int c;
                 char d;
                 double e;
                 bool f;
               } Node2;

BOOST_AUTO_TEST_SUITE(Structure)

BOOST_AUTO_TEST_CASE(Test1)
{
    Node n;
    BOOST_CHECK(sizeof(n) < 12);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(Union)

BOOST_AUTO_TEST_CASE(Test1)
{
    Node2 n;
    BOOST_CHECK(sizeof(n) == sizeof(double));
}

BOOST_AUTO_TEST_SUITE_END()

Listing 15에 있는 코드의 출력은 다음과 같다.


Listing 16. –log_level 옵션을 사용하여 여러 테스트 스위트 실행하기
	
[arpan@tintin] ./a.out --log_level=test_suite
Running 2 test cases...
Entering test suite "Regression"
Entering test suite "Structure"
Entering test case "Test1"
m2.cpp(23): error in "Test1": check sizeof(n) < 12 failed
Leaving test case "Test1"
Leaving test suite "Structure"
Entering test suite "Union"
Entering test case "Test1"
Leaving test case "Test1"
Leaving test suite "Union"
Leaving test suite "Regression"

*** 1 failure detected in test suite "Regression"


테스트 스위트 조직 이해하기

지금까지 이 기사에서는 계층 구조가 없는 테스트 스위트가 포함된 Boost 테스트 유틸리티에 대해 설명했다. 이제 외부 도구 사용자가 일반적으로 적절하다고 판단하는 방식으로 소프트웨어 제품을 테스트하는 Boost를 사용하여 테스트 스위트를 만들어보자. 테스트 프레임워크에는 일반적으로 각각 특정 제품 기능을 검사하는 여러 스위트가 있다. 예를 들어, 워드 프로세서의 회귀 프레임워크에는 글꼴 지원, 서로 다른 파일 형식 등에 대해 검사하는 스위트가 있어야 한다. 각각의 개별 테스트 스위트에는 여러 유닛 테스트가 포함된다. Listing 17에는 테스트 프레임워크의 예제가 제공된다. 코드의 시작점은 init_unit_test_suite라는 루틴이어야 한다.


Listing 17. 회귀 실행에 필요한 마스터 테스트 스위트 작성하기
	
#define BOOST_TEST_MODULE MasterTestSuite
#include <boost/test/included/unit_test.hpp>
using boost::unit_test;

test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
    test_suite* ts1 = BOOST_TEST_SUITE( "test_suite1" );
    ts1->add( BOOST_TEST_CASE( &test_case1 ) );
    ts1->add( BOOST_TEST_CASE( &test_case2 ) );

    test_suite* ts2 = BOOST_TEST_SUITE( "test_suite2" );
    ts2->add( BOOST_TEST_CASE( &test_case3 ) );
    ts2->add( BOOST_TEST_CASE( &test_case4 ) );

    framework::master_test_suite().add( ts1 );
    framework::master_test_suite().add( ts2 );

    return 0;
}

Listing 17에 있는 각 테스트 스위트(예: ts1)는 BOOST_TEST_SUITE 매크로를 사용하여 작성된다. 매크로는 테스트 스위트의 이름인 문자열을 예상한다. 모든 테스트 스위트는 결과적으로 add 메소드를 사용하여 마스터 테스트 스위트에 추가된다. 마찬가지로 BOOST_TEST_CASE 매크로를 사용하여 각 테스트를 작성한 후 다시 add 메소드를 사용하여 테스트 스위트에 추가한다. 또한 유닛 테스트를 마스터 테스트 스위트에 추가할 수도 있지만 이 방법은 권장되지 않는다. master_test_suite 메소드는 boost::unit_test::framework 네임스페이스의 일부로 정의되며 싱글톤을 내부적으로 구현한다. Boost 소스에서 제공되는 Listing 18에 있는 코드는 작동 방식에 대해 설명한다.


Listing 18. master_test_suite 메소드 이해하기
	
master_test_suite_t&
master_test_suite()
{
    if( !s_frk_impl().m_master_test_suite )
        s_frk_impl().m_master_test_suite = new master_test_suite_t;

    return *s_frk_impl().m_master_test_suite;
}

BOOST_TEST_CASE 매크로를 사용하여 작성되는 유닛 테스트는 함수 포인터를 입력 인수로 승인한다. 따라서 Listing 17에서 test_case1, test_case2 등은 사용자가 원하는 방식으로 코딩할 수 있는 void 함수이다. 하지만 Boost 테스트 설정에서는 힙 메모리의 상당 부분을 사용하기 때문에 BOOST_TEST_SUITE에 대한 모든 호출은 새로운 boost::unit_test::test_suite(<test suite name>)로 요약된다.


고정 기능

개념적으로 테스트 고정 기능은 테스트가 실행되기 전에 환경을 설정하고 테스트가 완료되면 정리하는 데 사용하기 위한 것이다. Listing 19에는 단순한 예제가 제공된다.


Listing 19. 기본 Boost 고정 기능
	
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <iostream>

struct F {
    F() : i( 0 ) { std::cout << "setup" << std::endl; }
    ~F()          { std::cout << "teardown" << std::endl; }

    int i;
};

BOOST_AUTO_TEST_SUITE( test )

BOOST_FIXTURE_TEST_CASE( test_case1, F )
{
    BOOST_CHECK( i == 1 );
    ++i;
}

BOOST_AUTO_TEST_SUITE_END()

Listing 20에는 출력이 표시된다.


Listing 20. Boost 고정 기능 사용의 출력
	
[arpan@tintin] ./a.out
Running 1 test case...
setup
fix.cpp(16): error in "test_case1": check i == 1 failed
teardown

*** 1 failure detected in test suite "example"

BOOST_AUTO_TEST_CASE 매크로를 사용하는 대신 이 코드에서는 추가 인수를 받는 BOOST_FIXTURE_TEST_CASE를 사용한다. 이 오브젝트의 constructordestructor 메소드는 필요한 설정 및 정리를 수행한다. Boost 헤더 unit_test_suite.hpp로 이를 확인한다(Listing 21 참조).


Listing 21. 헤더 unit_test_suite.hpp의 Boost 고정 기능 정의
	
#define BOOST_FIXTURE_TEST_CASE( test_name, F )       \
struct test_name : public F { void test_method(); };                   \
                                                                                        \
static void BOOST_AUTO_TC_INVOKER( test_name )()       \
{                                                                                       \
    test_name t;                                                                        \
    t.test_method();                                                                    \
}                                                                                       \
                                                                                        \
struct BOOST_AUTO_TC_UNIQUE_ID( test_name ) {};       \
                                                                                        \
BOOST_AUTO_TU_REGISTRAR( test_name )(                     \
    boost::unit_test::make_test_case(                                            \
        &BOOST_AUTO_TC_INVOKER( test_name ), #test_name ),                  \
    boost::unit_test::ut_detail::auto_tc_exp_fail<                                   \
        BOOST_AUTO_TC_UNIQUE_ID( test_name )>::instance()->value() );   \
                                                                                        \
void test_name::test_method()                                                           \

내부적으로 Boost는 struct F에서 클래스를 공개적으로 파생한 후(Listing 19 참조) 해당 클래스에서 오브젝트를 작성한다. C++에 있는 공용 상속 규칙과 같이 struct 클래스의 보호된 공용 변수는 모두 뒤따라오는 함수에서 직접 액세스할 수 있다. Listing 19에서 수정 중인 i 변수는 F 유형의 내부 오브젝트 t에 속하는 변수이다(Listing 20 참조). 몇 가지 테스트만 일부 명시적 초기화가 필요하기 때문에 고정 기능을 사용하는 회귀 스위트가 있는 것은 지극히 정상적이다. Listing 22에는 세 가지 테스트 중 하나만 고정 기능을 사용하는 테스트 스위트가 포함되어 있다.


Listing 22. 고정 기능 테스트와 비고정 기능 테스트가 혼합되어 있는 Boost 테스트 스위트
	
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <iostream>

struct F {
    F() : i( 0 ) { std::cout << "setup" << std::endl; }
    ~F()          { std::cout << "teardown" << std::endl; }

    int i;
};

BOOST_AUTO_TEST_SUITE( test )

BOOST_FIXTURE_TEST_CASE( test_case1, F )
{
    BOOST_CHECK( i == 1 );
    ++i;
}

BOOST_AUTO_TEST_CASE( test_case2 )
{
    BOOST_REQUIRE( 2 > 1 );
}

BOOST_AUTO_TEST_CASE( test_case3 )
{
    int i = 1;
    BOOST_CHECK_EQUAL( i, 1 );
    ++i;
}

BOOST_AUTO_TEST_SUITE_END()

Listing 22에는 개별 테스트 사례에서 정의되어 사용되는 고정 기능이 포함되어 있다. Boost에서도 사용자가 BOOST_GLOBAL_FIXTURE (<Fixture Name>) 매크로가 포함된 글로벌 고정 기능을 정의하고 사용할 수 있다. 글로벌 고정 기능은 수에 관계없이 정의할 수 있기 때문에 초기화 코드를 분할할 수 있다. Listing 23에서는 글로벌 고정 기능을 사용한다.


Listing 23. 회귀 초기화에 글로벌 고정 기능 사용하기
	
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <iostream>

struct F {
    F()           { std::cout << "setup" << std::endl; }
    ~F()         { std::cout << "teardown" << std::endl; }
};

BOOST_AUTO_TEST_SUITE( test )
BOOST_GLOBAL_FIXTURE( F );
BOOST_AUTO_TEST_CASE( test_case1 )
{
    BOOST_CHECK( true );
}
BOOST_AUTO_TEST_SUITE_END()

고정 기능이 여럿 있는 경우 설정 및 해제는 선언된 순서대로 발생한다. Listing 24에서 F의 생성자는 소멸자의 경우와 마찬가지로 F2 이전에 호출된다.


Listing 24. 회귀에 여러 글로벌 고정 기능 사용하기
	
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <iostream>

struct F {
    F()           { std::cout << "setup" << std::endl; }
    ~F()         { std::cout << "teardown" << std::endl; }
};

struct F2 {
    F2()           { std::cout << "setup 2" << std::endl; }
    ~F2()         { std::cout << "teardown 2" << std::endl; }
};

BOOST_AUTO_TEST_SUITE( test )
BOOST_GLOBAL_FIXTURE( F );
BOOST_GLOBAL_FIXTURE( F2 );

BOOST_AUTO_TEST_CASE( test_case1 )
{
    BOOST_CHECK( true );
}
BOOST_AUTO_TEST_SUITE_END()

글로벌 고정 기능을 개별 테스트의 오브젝트로 사용할 수는 없다. 또한 테스트 내에서 공용/보호된 비정적 메소드 또는 변수에 직접 액세스할 수 없다.


결론

이 기사의 내용은 여기까지이다. 이 기사에서는 가장 강력한 오픈 소스 회귀 프레임워크 중 하나인 Boost에 대해 소개했다. 여기서는 기본적인 Boost 검사, 패턴 일치, 부동 소수점 비교, 사용자 정의 검사, 테스트 스위트 조직—수동 및 자동—과 고정 기능에 대해 살펴봤다. Boost 문서에서 자세한 정보를 확인하도록 한다. 이 시리즈의 더 많은 기사에서는 cppUnit과 같은 다른 오픈 소스 회귀 프레임워크에 대해 소개한다.


참고자료

교육

제품 및 기술 얻기

  • Boost 테스트: Boost 프레임워크를 다운로드하자.

  • IBM 제품 평가판: DB2®, Lotus®, Rational®, Tivoli® 및 WebSphere®의 애플리케이션 개발 도구 및 미들웨어 제품을 사용해 보자.

토론

필자소개

Arpan Sen은 전자 설계 자동화 산업에서 소프트웨어 개발 분야에 근무하는 선임 엔지니어이다. 여러해 동안 Solaris, SunOS, HP-UX 및 IRIX를 포함한 UNIX는 물론 Linux와 Microsoft Windows의 여러 제품과 관련된 업무를 맡아 왔다. 소프트웨어 성능 최적화 기법, 그래프 이론 및 병렬 컴퓨팅에 많은 관심을 가지고 있다. Arpan은 소프트웨어 시스템 분야에서 대학원 과정을 마쳤다. 이메일 주소는 arpansen@gmail.com이다.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=AIX와 UNIX
ArticleID=474739
ArticleTitle=오픈 소스 C/C++ 유닛 테스트 도구, Part 1: Boost 유닛 테스트 프레임워크 알아보기
publish-date=12082009
author1-email=arpansen@gmail.com
author1-email-cc=mmccrary@us.ibm.com

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.