Buoni esempi di test unitari per sviluppatori C incorporati [chiuso]


20

La prossima settimana parlerò con il mio dipartimento del test unitario e dello sviluppo guidato dai test. Come parte di questo, mostrerò alcuni esempi reali di alcuni codici che ho scritto di recente, ma vorrei anche mostrare alcuni esempi molto semplici che scriverò nel discorso.

Ho cercato sul web buoni esempi, ma ho avuto difficoltà a trovare quelli che sono particolarmente applicabili alla nostra area di sviluppo. Quasi tutto il software che scriviamo è un sistema di controllo integrato che funziona su microcontrollori di piccole dimensioni. C'è un sacco di codice C che è facilmente applicabile ai test unitari (parlerò dei test unitari sul PC piuttosto che sul bersaglio stesso) fintanto che rimani libero dal livello "inferiore": le cose che parlano 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 Dive Into Python) e poiché non usiamo quasi mai stringhe, questo non è davvero adatto (riguardo alle uniche funzioni di libreria che il nostro codice utilizza in genere sono memcpy, memcmpe memset,strcat o le espressioni regolari non sono del tutto esatte).

Quindi, alla domanda: per favore qualcuno può offrire alcuni buoni esempi di funzioni che posso usare per dimostrare unit test in una sessione live? Una buona risposta secondo la mia opinione (soggetta a modifiche) sarebbe probabilmente:

  • Una funzione abbastanza semplice che chiunque (anche chi scrive solo occasionalmente codice) può capire;
  • Una funzione che non appare inutile (cioè elaborare la parità o CRC è probabilmente migliore di una funzione che moltiplica due numeri insieme e aggiunge una costante casuale);
  • Una funzione che è abbastanza breve da scrivere davanti a una stanza di persone (potrei approfittare dei molti appunti di Vim per ridurre gli errori ...);
  • Una funzione che accetta numeri, matrici, puntatori o strutture come parametri e restituisce qualcosa di simile, anziché gestire le stringhe;
  • Una funzione che presenta un semplice errore (ad es. >Anziché >=) che è facile da inserire e che comunque funzionerebbe nella maggior parte dei casi ma si spezzerebbe con un caso particolare: facile da identificare e correggere con un test unitario.

qualche idea?

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


Considerando il "problema" qui come una presentazione per vendere TDD al management, questo mi sembra ragionevolmente adatto al formato desiderato. L'OP sembra richiedere soluzioni esistenti a questo problema.
Technophile,

Risposte:


15

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 era stato verificato era il terminatore della stringa zero. Quindi puoi finire con l'inserimento di questa funzione di checksum nel codice e magari anche spedirla con essa e non notare mai un problema, cioè fino a quando non inizi ad applicarla a qualcosa di diverso dalle stringhe di testo.

Ecco un semplice test unit che segnalerà questo bug (la maggior parte 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);
    }
}

Molto buona! Questo è proprio il tipo di risposta che speravo: grazie.
DrAl

Quando crei il buffer hai già spazzatura in quel pezzo di memoria, è davvero necessario inizializzarlo con numeri casuali?
Snake Sanders,

@SnakeSanders Direi di sì, perché vuoi che i test unitari siano il più deterministici possibile. Se il compilatore che usi capita di mettere uno 0 sul tuo computer sviluppatore e un 10 sul tuo computer di prova, avrai un momento terribile a trovare il bug. Penso che farla dipendere dal tempo invece che da un seme fisso sia una cattiva idea, per lo stesso motivo.
Andrew dice Reintegrare Monica il

Affidarsi a comportamenti non deterministici in un unit test è una cattiva idea. Un test traballante ti farà venire il mal di testa prima o poi ...
sigy,

2

Che dire dell'implementazione di una funzione di ordinamento come il bubble sort ? Una volta che la funzione di ordinamento è attiva, è possibile continuare con la ricerca binaria, altrettanto utile per l'introduzione di unit test e TDD.

L'ordinamento e la ricerca dipendono dai confronti che è facile sbagliare. Implica anche lo scambio di puntatori attorno ai quali bisogna fare attenzione. Entrambi sono soggetti a errori, quindi sentiti libero di sbagliare :)

Qualche altra idea:

  • I test unitari aiutano molto quando si esegue il refactoring. Quindi, una volta che il tuo ordinamento a bolle funziona, potresti cambiarlo in un tipo più potente qsort, e i test dovrebbero comunque passare, dimostrando che anche la tua nuova funzione di ordinamento funziona.
  • L'ordinamento è facile da testare, il risultato è ordinato o no, il che lo rende un buon candidato.
  • Lo stesso per la ricerca; o esiste o non esiste.
  • 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, array enormi ecc.).

Hai qualche suggerimento specifico per un semplice errore che mostri come i test semplificano la vita?
DrAl,

@DrAl: aggiornata la mia risposta con quello.
Martin Wickman,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.