임베디드 C 개발자를위한 좋은 단위 테스트 예제 [닫기]


20

다음 주에 부서 테스트와 유닛 테스트 및 테스트 중심 개발에 대해 이야기 할 예정입니다. 이것의 일부로, 최근에 작성한 일부 코드에서 실제 예제를 보여 드리겠습니다. 그러나 대화에서 쓸 매우 간단한 예제도 보여주고 싶습니다.

웹에서 좋은 예를 찾고 있었지만 개발 영역에 특히 적합한 것을 찾기 위해 고심하고 있습니다. 우리가 작성하는 거의 모든 소프트웨어는 소형 마이크로 컨트롤러에서 실행되는 깊게 내장 된 제어 시스템입니다. 'bottom'레이어에서 벗어나지 않는 한 단위 테스트에 쉽게 적용 할 수있는 많은 C 코드가 있습니다 (목표 자체가 아닌 PC에서 단위 테스트에 대해 이야기 할 것입니다). 마이크로 컨트롤러 주변 장치에 그러나 내가 찾은 대부분의 예제는 문자열 처리 (예 : 우수한 Din Into Python Roman numerics 예제)를 기반으로하는 경향이 있으며 문자열을 거의 사용하지 않기 때문에 실제로는 적합하지 않습니다 (코드가 일반적으로 사용하는 유일한 라이브러리 함수에 대해) 있다 memcpy, memcmp그리고 memset,strcat 또는 정규 표현식이 옳지 않습니다).

그래서 질문에 : 누구나 라이브 세션에서 단위 테스트를 시연하는 데 사용할 수있는 좋은 기능 예를 제공 할 수 있습니까? 내 의견에 대한 좋은 대답은 아마도 다음과 같습니다.

  • 누구나 (때때로 코드를 작성하는 사람들조차도) 이해할 수있을만큼 간단한 기능;
  • 무의미하게 보이지 않는 함수 (즉, 패리티 또는 CRC를 계산하는 것이 두 숫자를 곱하고 임의의 상수를 추가하는 함수보다 낫습니다).
  • 사람들 앞에서 방에 쓸 수있을 정도로 짧은 기능 (오류를 줄이기 위해 Vim의 많은 클립 보드를 활용할 수 있습니다 ...);
  • 숫자, 배열, 포인터 또는 구조를 매개 변수로 사용하고 문자열을 처리하는 대신 비슷한 것을 반환하는 함수입니다.
  • 입력하기 쉬운 간단한 오류 (예 : >대신 >=) 가있는 함수 는 대부분의 경우 여전히 작동하지만 특정 경우와 관련이 있습니다 : 단위 테스트로 쉽게 식별하고 수정하십시오.

이견있는 사람?

아마도 관련이 없지만 테스트 자체는 Google Test Framework를 사용하여 C ++로 작성 될 것입니다. 모든 헤더에는 이미 #ifdef __cplusplus extern "C" {래퍼가 있습니다. 이것은 내가 지금까지 한 테스트에서 잘 작동했습니다.


여기에서 "문제"를 TDD를 경영진에게 판매하기위한 프레젠테이션으로 생각하면 원하는 형식에 합리적으로 잘 맞는 것 같습니다. OP가이 문제에 대한 기존 솔루션을 요청하는 것 같습니다.
Technophile

답변:


15

다음은 len 바이트에 걸쳐 체크섬을 생성하는 간단한 함수입니다 .

int checksum(void *p, int len)
{
    int accum = 0;
    unsigned char* pp = (unsigned char*)p;
    int i;
    for (i = 0; i <= len; i++)
    {
        accum += *pp++;
    }
    return accum;
}

그것은 울타리 게시물 버그가 있습니다 : for 문에서 테스트는이어야합니다 i < len.

재미있는 점은 다음과 같은 텍스트 문자열에 적용하면 ...

char *myString = "foo";
int testval = checksum(myString, strlen(myString));

당신은 "정답"을 얻을 것이다! 체크섬 된 여분의 바이트가 0 문자열 종결 자이기 때문입니다. 따라서이 체크섬 함수를 코드에 넣을 수 있고, 심지어 코드와 함께 제공 될 수도 있으며, 텍스트 문자열 이외의 것에 적용하기 시작할 때까지 문제를 발견하지 못할 수 있습니다.

다음은이 버그를 신고하는 간단한 단위 테스트입니다 (대부분의 경우 ... :-)

void main()
{
    // Seed the random number generator
    srand(time(NULL));

    // Fill an array with junk bytes
    char buf[1024];
    int i;
    for (i = 0; i < 1024; i++)
    {
        buf[i] = (char)rand();
    }

    // Put the numbers 0-9 in the first ten bytes
    for (i = 0; i <= 9; i++)
    {
        buf[i] = i;
    }

    // Now, the unit test. The sum of 0 to 9 should
    // be 45. But if buf[10] isn't 0 - which it won't be,
    // 255/256 of the time - this will fail.
    int testval = checksum(buf, 10);
    if (testval == 45)
    {
        printf("Passed!\n");
    }
    else
    {
        printf("Failed! Expected 45, got %d\n", testval);
    }
}

아주 좋아요! 이것은 내가 기대했던 일종의 답변입니다. 감사합니다.
DrAl

버퍼를 만들 때 이미 해당 메모리 덩어리에 쓰레기가 있으면 임의의 숫자로 초기화해야합니까?
뱀 샌더스

@SnakeSanders 단위 테스트를 가능한 한 결정적이기를 원하기 때문에 그렇습니다. 사용하는 컴파일러가 개발자 시스템에 0을, 테스트 시스템에 10을 넣으면 버그를 찾는 데 끔찍한 시간이 있습니다. 같은 이유로 고정 씨앗 대신 시간에 의존하게 만드는 것은 나쁜 생각이라고 생각합니다.
앤드류는 모니카 복원 모니카

단위 테스트에서 비 결정적 동작에 의존하는 것은 나쁜 생각입니다. 색다른 테스트는 조만간 두통을
유발할 것이다

2

거품 정렬 과 같은 정렬 함수를 구현하는 것은 어떻습니까? 정렬 기능이 작동하면 단위 테스트 및 TDD를 소개하는 데 적합한 이진 검색 을 계속할 수 있습니다 .

정렬 및 검색은 잘못되기 쉬운 비교에 따라 다릅니다. 또한주의를 기울여 수행해야 할 포인터를 교체합니다. 둘 다 오류가 발생하기 쉽습니다. :)

몇 가지 더 아이디어 :

  • 단위 테스트는 리팩토링을 수행 할 때 많은 도움이됩니다. 따라서 거품 정렬이 작동하면을 (를)보다 강력한 정렬로 변경할 수 qsort있으며 테스트는 여전히 통과해야하며 새로운 정렬 기능도 작동합니다.
  • 정렬은 테스트하기 쉽고 결과가 정렬되거나 정렬되지 않으므로 좋은 후보가됩니다.
  • 검색도 마찬가지입니다. 존재하거나 존재하지 않습니다.
  • 정렬을 위해 테스트를 작성하면 테스트에 사용할 입력 유형 (제로 요소, 임의 입력, 중복 항목, 거대한 배열 등)과 같은 토론이 열립니다.

테스트를 통해 인생을 더 쉽게 만드는 방법을 보여주는 간단한 오류에 대한 구체적인 제안이 있습니까?
DrAl

@ DrAl : 내 대답을 업데이트했습니다.
Martin Wickman 12
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.