Esempi di test di buona unità per sviluppatori C incorporati [chiuso]

17

La prossima settimana parlerò con il mio dipartimento dei test unitari e dello sviluppo basato sui test. Come parte di questo, ho intenzione di mostrare alcuni esempi del mondo reale da qualche codice che ho scritto di recente, ma mi piacerebbe anche mostrare alcuni esempi molto semplici che scriverò nel talk.

Ho cercato sul Web buoni esempi, ma ho cercato di trovare quelli che sono particolarmente applicabili alla nostra area di sviluppo. Quasi tutto il software che scriviamo è un sistema di controllo profondamente integrato che funziona su microcontrollori di piccole dimensioni. C'è un sacco di codice C che è facilmente applicabile al test delle unità (parlerò dei test delle unità sul PC piuttosto che sul bersaglio stesso) a patto che tu stia lontano dal livello "in basso": il materiale che parla direttamente alle periferiche del microcontrollore. Tuttavia, la maggior parte degli esempi che ho trovato tendono ad essere basati sull'elaborazione di stringhe (ad esempio l'eccellente esempio di numeri romani di Dive Into Python) e dal momento che non utilizziamo quasi mai le stringhe questo non è particolarmente adatto (riguardo alle sole funzioni di libreria che il nostro codice utilizza in genere sono memcpy , memcmp e memset , quindi qualcosa basato su strcat o espressioni regolari non è del tutto corretto).

Quindi, alla domanda: per favore qualcuno può offrire alcuni buoni esempi di funzioni che posso usare per dimostrare i test unitari in una sessione live? Una buona risposta nel mio (soggetto a cambiamento) opinione sarebbe probabilmente:

  • Una funzione abbastanza semplice da consentire a chiunque (anche a coloro che scrivono codice solo occasionalmente);
  • Una funzione che non appare inutile (ad esempio, elaborare la parità o CRC è probabilmente meglio di una funzione che moltiplica due numeri insieme e aggiunge una costante casuale);
  • Una funzione abbastanza breve da scrivere di fronte a una stanza di persone (posso trarre vantaggio dai numerosi Appunti di Vim per ridurre gli errori ...);
  • Una funzione che accetta numeri, matrici, puntatori o strutture come parametri e restituisce qualcosa di simile, piuttosto che gestire stringhe;
  • Una funzione che ha un semplice errore (ad esempio > piuttosto che >= ) facile da inserire che funzionerebbe ancora nella maggior parte dei casi ma che si interromperebbe con alcuni casi limite: facile da identificare e correggere con un test unitario.

Qualche idea?

Anche se probabilmente non è pertinente, i test stessi verranno probabilmente scritti in C ++ usando il Google Test Framework: tutti i nostri header hanno già il wrapper #ifdef __cplusplus extern "C" { attorno a loro; questo ha funzionato bene con i test che ho fatto finora.

    
posta DrAl 26.05.2011 - 11:02
fonte

2 risposte

14

Ecco una semplice funzione che dovrebbe generare un checksum su len byte.

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;
}

Ha un bug di fencepost: nell'istruzione for, il test dovrebbe essere i < len .

La cosa divertente è che se la applichi a una stringa di testo come questa ...

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

otterrai la "risposta giusta"! Questo perché il byte extra che è stato eseguito il checksum è stato il terminatore a stringa zero. In questo modo è possibile impostare la funzione di checksum in codice e magari persino inviarla e non notare mai un problema, ovvero fino a quando non si inizia ad applicarlo a qualcosa di diverso dalle stringhe di testo.

Ecco un test unitario semplice che contrassegnerà questo bug (il più delle volte ...: -)

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);
    }
}
    
risposta data 01.06.2011 - 07:28
fonte
2

Che ne pensi di implementare una funzione di ordinamento come ordinamento a bolle ? Una volta che hai funzionato la funzione di ordinamento, puoi continuare con ricerca binaria che è esattamente come buono per l'introduzione di test unitari e TDD.

L'ordinamento e la ricerca dipendono da confronti facili da sbagliare. Significa anche scambiare i puntatori attorno ai quali bisogna fare attenzione. Entrambi sono inclini agli errori, quindi sentitevi liberi di rovinare:)

Altre idee in più:

  • I test unitari aiutano molto quando si esegue il refactoring. Quindi, una volta che il tuo ordinamento di bolle funziona, potresti cambiarlo in un tipo più potente come qsort , e i test dovrebbero ancora passare, dimostrando che anche la tua nuova funzione di ordinamento funziona.
  • L'ordinamento è facile da testare, il risultato è ordinato o non lo è, il che lo rende un buon candidato.
  • Lo stesso per la ricerca; esiste o no.
  • La scrittura di test per l'ordinamento apre discussioni come il tipo di input da utilizzare per il test (zero elementi, input casuali, voci duplicate, enormi matrici, ecc.)
risposta data 26.05.2011 - 12:55
fonte

Leggi altre domande sui tag