홈: https://github.com/mity/acutest
Acutest는 개발자의 길을 방해하지 않고 외부 종속성을 최소화하기 위해 가능한 한 단순함을 목표로 하는 C/C++ 단위 테스트 시설입니다.
이를 달성하기 위해 완전한 구현은 단일 C 헤더 파일에 상주하며 그 핵심은 몇 가지 표준 C 라이브러리 함수에만 의존합니다.
주요 특징:
acutest.h
라는 단일 헤더 파일입니다.main()
)을 제공합니다.--tap
옵션 사용)--xml-output=FILE
사용)C++ 특정 기능:
std::exception
에서 파생된 경우 what()
오류 메시지에 기록됩니다.Unix/Posix 특정 기능:
clock_gettime()
)를 제공하는 경우 사용자는 --time=real
( --time
과 동일) 및 --time=cpu
사용하여 테스트 실행 시간을 측정할 수 있습니다.Linux 특정 기능:
Windows 특정 기능:
--time
사용하여 테스트 실행 시간을 측정할 수 있습니다.macOS 특정 기능:
acutest.h
포함하여 하나 이상의 단위 테스트를 구현하는 모든 C/C++ 모듈은 독립 실행형 프로그램으로 구축될 수 있습니다. 우리는 이 문서의 목적에 따라 결과 바이너리를 "테스트 모음"이라고 부릅니다. 그런 다음 해당 명령줄 옵션에 지정된 대로 제품군이 실행되어 테스트를 실행합니다.
다음과 같은 경우에만 단위 테스트가 성공한다고 말합니다.
Acutest를 사용하려면 하나 이상의 단위 테스트를 구현하는 C/C++ 소스 파일 시작 부분에 헤더 파일 acutest.h
포함하면 됩니다. 헤더는 main()
함수의 구현을 제공합니다.
#include "acutest.h"
모든 테스트는 다음 프로토타입을 사용하여 함수로 구현되어야 합니다.
void test_example ( void );
테스트에서는 일부 전처리기 매크로를 사용하여 테스트 조건을 검증할 수 있습니다. 여러 번 사용할 수 있으며 해당 조건 중 하나라도 실패하면 특정 테스트는 실패한 것으로 간주됩니다.
TEST_CHECK
는 단순히 부울 조건을 테스트하고 조건이 false(또는 0)로 평가되면 실패하는 가장 일반적으로 사용되는 테스트 매크로입니다.
예를 들어:
void test_example ( void )
{
void * mem ;
int a , b ;
mem = malloc ( 10 );
TEST_CHECK ( mem != NULL );
mem = realloc ( mem , 20 );
TEST_CHECK ( mem != NULL );
}
테스트는 서로 완전히 독립적이어야 합니다. 테스트 스위트가 호출될 때마다 사용자는 스위트에서 어떤 순서로든 원하는 수의 테스트를 실행할 수 있습니다. 또한 기본적으로 지원되는 플랫폼에서는 각 단위 테스트가 독립형 (하위) 프로세스로 실행됩니다.
마지막으로 테스트 스위트 소스 파일은 TEST_LIST
매크로를 사용하여 단위 테스트를 나열해야 합니다. 목록은 각 테스트의 이름(고유해야 함)과 테스트를 구현하는 함수에 대한 포인터를 지정합니다. 나는 명령줄에서 사용하기 쉬운 이름을 추천합니다. 특히 쉘에서 이스케이프가 필요할 수 있는 공백 및 기타 특수 문자는 피하십시오. 또한 대시( -
)를 이름의 첫 번째 문자로 사용하지 마십시오. 그러면 테스트 이름이 아닌 명령줄 옵션으로 해석될 수 있습니다.
TEST_LIST = {
{ "example" , test_example },
...
{ NULL, NULL } /* zeroed record marking the end of the list */
};
테스트 목록은 0으로 된 기록으로 끝나야 합니다.
기본 테스트 스위트의 경우 이것이 알아야 할 전부입니다. 그러나 Acutetest는 일부 특정 상황에서 유용할 수 있는 몇 가지 매크로를 더 제공합니다. 다음 하위 섹션에서 이에 대해 다룹니다.
TEST_CHECK
와 매우 유사한 매크로 TEST_ASSERT
있지만, 실패하면 현재 단위 테스트 실행을 즉시 중단합니다.
예를 들어:
void test_example ( void )
{
void * mem ;
int a , b ;
mem = malloc ( 10 );
TEST_ASSERT ( mem != NULL );
mem = realloc ( mem , 20 );
TEST_ASSERT ( mem != NULL );
}
실패한 경우 중단은 abort()
(테스트가 하위 프로세스로 실행되는 경우)를 호출하거나 longjmp()
(그렇지 않은 경우)를 통해 수행됩니다.
따라서 이러한 잔인한 테스트 중단과 관련된 비용을 이해하는 경우에만 사용해야 합니다. 단위 테스트가 수행하는 작업에 따라 플러시되지 않은 파일 설명자, 메모리 누수, 소멸자 호출 없이 파괴된 C++ 객체 등이 포함될 수 있습니다.
일반적으로 자신이 하는 일과 특정 상황에서 TEST_ASSERT
선택한 이유를 정확히 알지 않는 한 TEST_ASSERT
보다 TEST_CHECK
선호해야 합니다.
C++의 경우 지정된 코드(일반적으로 함수 또는 메서드 호출)가 예상되는 유형의 예외를 발생시키는지 확인하기 위한 추가 매크로 TEST_EXCEPTION
이 있습니다.
함수가 예외를 발생시키지 않거나 호환되지 않는 항목을 발생시키는 경우 검사가 실패합니다.
예를 들어:
void test_example ( void )
{
TEST_EXCEPTION ( CallSomeFunction (), std:: exception );
}
조건 확인이 실패하는 경우 문제를 더 쉽게 디버깅할 수 있도록 상황에 대한 추가 정보를 제공하는 것이 유용한 경우가 많습니다. Acutest는 이러한 목적으로 TEST_MSG
및 TEST_DUMP
매크로를 제공합니다.
전자는 printf
와 유사한 메시지를 출력하고, 다른 하나는 제공된 메모리 블록의 16진수 덤프를 출력합니다.
예를 들면 다음과 같습니다.
void test_example ( void )
{
char produced [ 100 ];
char expected [] = "Hello World!" ;
SomeSprintfLikeFunction ( produced , "Hello %s!" , "world" );
TEST_CHECK ( strcmp ( produced , expected ) == 0 );
TEST_MSG ( "Expected: %s" , expected );
TEST_MSG ( "Produced: %s" , produced );
/* Or, if the function could provide some binary stuff, we might rather
* use TEST_DUMP instead in order to output a hexadecimal dump of the data.
*/
TEST_DUMP ( "Expected:" , expected , strlen ( expected ));
TEST_DUMP ( "Produced:" , produced , strlen ( produced ));
}
두 매크로 모두 가장 최근에 확인한 매크로가 실패한 경우에만 아무 것도 출력하지 않습니다. 즉, 이 두 가지는 동일합니다.
if (! TEST_CHECK ( some_condition != 0 ))
TEST_MSG ( "some message" );
TEST_CHECK ( some_condition != 0 );
TEST_MSG ( "some message" );
( TEST_MSG
에는 가변 매크로를 지원하는 컴파일러가 필요합니다.)
때로는 테스트 벡터 모음과 각각의 예상 출력을 제공하는 데이터에 대한 루프로 테스트 기능을 설계하는 것이 유용합니다. 예를 들어 단위 테스트가 일종의 해싱 함수 구현을 확인해야 하고 해시 사양에 이에 대한 테스트 벡터 컬렉션이 있다고 가정해 보겠습니다.
이러한 경우 모든 테스트 벡터와 관련된 이름을 가져와 출력 로그에 이름을 출력하면 검사가 실패할 경우 유죄 테스트 벡터를 쉽게 식별할 수 있어 매우 유용합니다. 그러나 루프 본문은 수십 개의 확인 매크로를 실행할 수 있으므로 루프의 모든 확인 메시지를 사용자 정의하기 위해 이러한 이름을 추가하는 것은 비현실적일 수 있습니다.
이 문제를 해결하기 위해 Acutest는 TEST_CASE
매크로를 제공합니다. 매크로는 테스트 벡터 이름으로 사용되는 문자열을 지정합니다. Acutetest를 사용하면 출력 로그에서 제공된 이름이 후속 조건 확인의 메시지 앞에 오는지 확인합니다.
예를 들어, 어떤 크기의 주어진 바이트 배열에 대해 새로 malloc
된 버퍼에 다른 바이트 배열을 반환한다고 가정하는 SomeFunction()
테스트한다고 가정해 보겠습니다. 그러면 우리는 다음과 같이 할 수 있습니다:
struct TestVector {
const char * name ;
const uint8_t * input ;
size_t input_size ;
const uint8_t * expected_output ;
size_t expected_output_size ;
};
struct TestVector test_vectors [] = {
/* some data */
};
void test_example ( void )
{
int i ;
const uint8_t * output ;
size_t output_size ;
for ( i = 0 ; i < sizeof ( test_vectors ) / sizeof ( test_vectors [ 0 ]); i ++ ) {
struct TestVector * vec = & test_vectors [ i ];
/* Output the name of the tested test vector. */
TEST_CASE ( vec . name );
/* Now, we can check the function produces what it should for the
* current test vector. If any of the following checking macros
* produces any output (either because the check fails, or because
* high `--verbose` level is used), Acutest also outputs the currently
* tested vector's name. */
output = SomeFunction ( vec -> input , vec -> input_size , & output_size );
if ( TEST_CHECK ( output != NULL )) {
TEST_CHECK ( output_size == vec -> expected_output_size );
TEST_CHECK ( memcmp ( output , vec -> expected_output , output_size ) == 0 );
free ( output );
}
}
}
지정된 이름은 TEST_CASE
사용 후 실행되는 모든 검사에 적용됩니다.
TEST_CASE
다시 사용하여 다른 이름을 지정할 때까지; 또는NULL
을 인수로 사용하여 TEST_CASE
사용하여 이름이 명시적으로 재설정될 때까지.이전 섹션에서 언급된 많은 매크로에는 일부 기본 메시지 대신 사용자 정의 메시지를 출력할 수 있는 대응 항목이 있습니다.
이들 모두는 밑줄 접미사가 추가된 것 외에는 앞서 언급한 매크로와 이름이 동일합니다. 접미사를 사용하면 printf
와 유사한 문자열 형식과 해당 추가 인수가 필요합니다.
예를 들어 간단한 확인 매크로 대신
TEST_CHECK (a == b);
TEST_ASSERT (x < y);
TEST_EXCEPTION (SomeFunction(), std::exception);
사용자 정의 메시지와 함께 해당 대응 항목을 사용할 수 있습니다.
TEST_CHECK_ (a == b, " %d is equal to %d " , a, b);
TEST_ASSERT_ (x < y, " %d is lower than %d " , x, y);
TEST_EXCEPTION_ (SomeFunction(), std::exception, "SomeFunction() throws std::exception");
명령줄 옵션 --verbose
를 사용하면 해당 검사가 성공적으로 통과하더라도 메시지가 로그아웃되므로 중립적인 표현을 사용해야 합니다.
(검사가 실패할 경우를 대비하여 일부 진단 정보를 출력해야 하는 경우 TEST_MSG
매크로를 사용하십시오. 이것이 바로 그 목적입니다.)
마찬가지로, 대신
TEST_CASE ( "name" );
우리는 더 풍부하게 사용할 수 있습니다
TEST_CASE_ ( "iteration #%d" , 42 );
그러나 이들 모두는 컴파일러가 가변 전처리기 매크로를 지원하는 경우에만 사용할 수 있습니다. Variadic 매크로는 C99를 통해 C 언어의 표준 부분이 되었습니다.
테스트 구현이 끝나면 간단한 C/C++ 프로그램으로 간단히 컴파일할 수 있습니다. 예를 들어, cc
가 C 컴파일러라고 가정합니다.
$ cc test_example.c -o test_example
테스트 스위트가 컴파일되면 결과 테스트 바이너리를 사용하여 테스트를 실행할 수 있습니다.
실행된 단위 테스트가 모두 통과하면 테스트 스위트의 종료 코드는 0이고, 그 중 하나라도 실패하면 1이며, 내부 오류가 발생하면 다른 숫자입니다.
기본적으로(명령줄 옵션 없이) 구현된 모든 단위 테스트를 실행합니다. 또한 명령줄에 지정된 대로 단위 테스트의 하위 집합만 실행할 수도 있습니다.
$ ./test_example # Runs all tests in the suite
$ ./test_example test1 test2 # Runs only tests specified
$ ./test_example --exclude test3 # Runs all tests but those specified
Acutetest는 여러 수준의 단위 테스트 선택을 구현하기 때문에 단일 명령줄 인수로 전체 테스트 단위 그룹을 선택할 수 있습니다(적어도 하나의 테스트 단위와 일치하는 첫 번째 항목이 사용됨).
정확한 일치 : 인수가 단위 테스트의 전체 이름과 정확히 일치하면 주어진 테스트만 선택됩니다.
단어 일치 : 인수가 전체 테스트 이름과 일치하지 않지만 하나 이상의 테스트 이름에서 전체 단어와 일치하는 경우 해당 테스트가 모두 선택됩니다.
다음 문자는 단어 구분 기호로 인식됩니다. 공백
, 표 작성기 t
, 대시 -
, 밑줄 _
, 슬래시 /
, 점 .
, 쉼표 ,
, 콜론 :
, 세미콜론 ;
.
하위 문자열 일치 : 단어 일치조차도 테스트를 선택하지 못한 경우 하위 문자열로 인수가 포함된 이름을 가진 모든 테스트가 선택됩니다.
적절한 테스트 명명 전략을 채택하면 사용자가 단일 명령줄 인수를 사용하여 관련 테스트 전체 제품군을 쉽게 실행할 수 있습니다(또는 --exclude
사용하는 경우 건너뛸 수 있음).
예를 들어 테스트 foo-1
, foo-2
, foomatic
, bar-1
및 bar-10
을 구현하는 테스트 스위트 test_example
고려해보세요.
$ ./test_example bar-1 # Runs only the test 'bar-1' (exact match)
$ ./test_example foo # Runs 'foo-1' and 'foo-2' (word match)
$ ./test_example oo # Runs 'foo-1', 'foo-2' and 'foomatic' (substring match)
$ ./test_example 1 # Runs 'foo-1' and 'bar-1' (word match)
--list
또는 -l
사용하여 지정된 테스트 모음에서 구현된 모든 단위 테스트를 나열할 수 있습니다.
$ ./test_example --list
지원되는 모든 명령줄 옵션에 대한 설명을 보려면 --help
옵션을 사용하여 바이너리를 실행하세요.
$ ./test_example --help
Q: 이 프로젝트는 'CUTest'라는 이름이 아니었나요?
답: 그렇습니다. 원래 이름이 너무 과부화되어 있는 것으로 확인되어 이름을 바꾸었습니다.
Q: README.md
및/또는 LICENSE.md
파일을 배포해야 합니까?
A: 아니요. acutest.h
헤더에는 리포지토리에 대한 URL, 저작권 정보 및 그 안에 있는 MIT 라이선스 조건이 포함되어 있습니다. 이를 그대로 유지하는 한, 프로젝트에 헤더만 추가해도 전혀 문제가 없습니다. 결국, 간단한 사용과 올인원 헤더 특성이 우리의 주요 목표입니다.
Acutest는 MIT 라이선스로 보호됩니다. 전체 텍스트를 보려면 LICENSE.md
파일이나 acutest.h
의 시작 부분을 참조하세요.
프로젝트는 github에 있습니다:
그곳에서 Acutest의 최신 버전을 찾고, 개선 사항에 기여하거나, 버그를 보고할 수 있습니다.