unit-testing unity - Codice C Test unitario




ignore (25)

Ho lavorato su un sistema embedded questa estate scritto in modo diretto C. Era un progetto esistente che la società per cui lavoravo aveva rilevato. Mi sono abbastanza abituato a scrivere unit test in Java usando JUnit, ma ero in perdita per quanto riguarda il modo migliore per scrivere test unitari per il codice esistente (che richiedeva il refactoring) così come il nuovo codice aggiunto al sistema.

Esiste un modo per rendere il test del codice C semplice come un'unità testando il codice Java con, ad esempio, JUnit ? Qualsiasi intuizione che si applichi specificamente allo sviluppo integrato (cross-compiling alla piattaforma arm-linux) sarebbe molto apprezzata.


Answers

LibU ( http://koanlogic.com/libu ) ha un modulo di test unitario che consente esplicite dipendenze di casi / suite di test, isolamento del test, esecuzione parallela e un formattatore di report personalizzabile (i formati predefiniti sono xml e txt).

La libreria è dotata di licenza BSD e contiene molti altri moduli utili: reti, debug, strutture dati comunemente utilizzate, configurazione, ecc., Se ne hai bisogno nei tuoi progetti ...


Il libro di Michael Feather "Lavorare efficacemente con il codice legacy" presenta molte tecniche specifiche per i test unitari durante lo sviluppo di C.

Esistono tecniche correlate all'iniezione di dipendenza specifiche per C che non ho visto da nessun'altra parte.


Dopo aver letto Minunit ho pensato che un modo migliore era basare il test in macro assert che uso molto come tecnica del programma difensivo. Quindi ho usato la stessa idea di Minunit mista a quella standard. Puoi vedere il mio framework (un buon nome potrebbe essere NoMinunit) nel blog di k0ga


Ho usato RCUNIT per fare alcuni test unitari per codice incorporato su PC prima di testare il target. Una buona astrazione dell'interfaccia hardware è importante, altrimenti i registri mappati in memoria ti uccideranno.


Nel caso tu stia bersagliando piattaforme Win32 o modalità kernel NT, dovresti dare un'occhiata a cfix .


Si potrebbe anche voler dare un'occhiata a libtap , un framework di test C che emette il Test Anything Protocol (TAP) e quindi si integra bene con una varietà di strumenti che escono per questa tecnologia. È usato principalmente nel mondo della lingua dinamica, ma è facile da usare e sta diventando molto popolare.

Un esempio:

#include <tap.h>

int main () {
    plan(5);

    ok(3 == 3);
    is("fnord", "eek", "two different strings not that way?");
    ok(3 <= 8732, "%d <= %d", 3, 8732);
    like("fnord", "f(yes|no)r*[a-f]$");
    cmp_ok(3, ">=", 10);

    done_testing();
}

Non ho fatto molto per testare un'applicazione C legacy prima di iniziare a cercare un modo per simulare le funzioni. Mi servivano male i mock per isolare il file C che voglio testare dagli altri. Ho dato una prova di cmock e penso che lo adotterò.

Cmock analizza i file di intestazione e genera funzioni di simulazione basate sui prototipi che trova. Mock ti permetterà di testare un file C in perfetto isolamento. Tutto quello che dovrai fare è collegare il tuo file di test con mock invece dei tuoi file di oggetti reali.

Un altro vantaggio di cmock è che convaliderà i parametri passati a funzioni fittizie, e ti permetterà di specificare quale valore di ritorno devono fornire i mock. Questo è molto utile per testare diversi flussi di esecuzione nelle tue funzioni.

I test consistono delle tipiche funzioni testA (), testB () in cui si creano aspettative, chiamate funzioni per testare e verificare asserzioni.

L'ultimo passo è generare un corridore per i tuoi test con l'unità. Cmock è legato al quadro di prova dell'unità. Unity è facile da imparare come qualsiasi altro framework di test unitario.

Vale la pena provare e abbastanza facile da capire:

http://sourceforge.net/apps/trac/cmock/wiki

Aggiornamento 1

Un altro quadro che sto studiando è Cmockery.

http://code.google.com/p/cmockery/

Si tratta di un puro framework C che supporta il testing e la simulazione delle unità. Non ha dipendenza dal ruby ​​(contrariamente a Cmock) e ha pochissima dipendenza dalle librerie esterne.

Richiede un po 'più di lavoro manuale per configurare i mock perché non genera codice. Questo non rappresenta molto lavoro per un progetto esistente poiché i prototipi non cambieranno molto: una volta che hai i tuoi mock, non avrai bisogno di cambiarli per un po '(questo è il mio caso). La tipizzazione aggiuntiva fornisce il controllo completo dei mock. Se c'è qualcosa che non ti piace, cambi semplicemente la tua finta.

Non c'è bisogno di un corridore di prova speciale. Hai solo bisogno di creare una serie di test e passarla a una funzione run_tests. Un po 'più di lavoro manuale anche qui, ma mi piace decisamente l'idea di un quadro autonomo autonomo.

Inoltre contiene alcuni trucchi C che non conoscevo.

Nel complesso Cmockery ha bisogno di un po 'più di comprensione dei mock per iniziare. Gli esempi dovrebbero aiutarti a superare questo. Sembra che possa fare il lavoro con una meccanica più semplice.


Cmockery è un progetto lanciato di recente che consiste in una libreria C molto semplice da utilizzare per i test delle unità di scrittura.


Personalmente mi piace il framework di Google Test .

La vera difficoltà nel test del codice C è la rottura delle dipendenze su moduli esterni in modo da poter isolare il codice in unità. Questo può essere particolarmente problematico quando si sta tentando di eseguire test intorno al codice legacy. In questo caso mi capita spesso di utilizzare il linker per utilizzare le funzioni di stub nei test.

Questo è ciò a cui le persone si riferiscono quando parlano di " cuciture ". In C l'unica opzione è usare il pre-processore o il linker per deridere le tue dipendenze.

Una tipica suite di test in uno dei miei progetti C potrebbe essere simile a questa:

#include "myimplementationfile.c"
#include <gtest/gtest.h>

// Mock out external dependency on mylogger.o
void Logger_log(...){}

TEST(FactorialTest, Zero) {
    EXPECT_EQ(1, Factorial(0));
}

Si noti che in realtà si include il file C e non il file di intestazione . Ciò offre il vantaggio dell'accesso a tutti i membri dei dati statici. Qui metto in pausa il mio logger (che potrebbe essere in logger.o e dare un'implementazione vuota. Ciò significa che il file di test compila e collega in modo indipendente dal resto della base di codice ed esegue in isolamento.

Per quanto riguarda la compilazione incrociata del codice, affinché funzioni, è necessario disporre di buone strutture sul target. Ho fatto questo con googletest cross compilato su Linux su un'architettura PowerPC. Questo ha senso perché ci sono una shell completa e un sistema operativo per raccogliere i risultati. Per gli ambienti meno ricchi (che classifico come qualcosa senza un sistema operativo completo), dovresti semplicemente creare ed eseguire l'host. Dovresti farlo comunque in modo da poter eseguire i test automaticamente come parte della build.

Trovo che testare il codice C ++ sia in genere molto più semplice a causa del fatto che il codice OO è in generale molto meno abbinato rispetto a quello procedurale (ovviamente dipende molto dallo stile di codifica). Anche in C ++ puoi usare trucchi come l'integrazione delle dipendenze e il metodo di sovrascrivere per ottenere cuciture in codice che è altrimenti incapsulato.

Michael Feathers ha un eccellente libro sul test del codice legacy . In un capitolo tratta le tecniche per trattare il codice non OO che consiglio vivamente.

Modifica : ho scritto un post sul blog sul codice procedurale di test unitario, con l' origine disponibile su GitHub .

Modifica : C'è un nuovo libro che esce dai programmatori pragmatici che si rivolge specificamente al codice C di test unitario che consiglio vivamente .


API Sanity Checker - framework di test per librerie C / C ++:

Un generatore automatico di test unitari di base per una libreria C / C ++ condivisa. È in grado di generare dati di input ragionevoli (nella maggior parte, ma purtroppo non tutti) per i parametri e di compilare semplici casi di test ("sanity" o "shallow") per ogni funzione nell'API attraverso l'analisi delle dichiarazioni nell'intestazione File.

La qualità dei test generati consente di verificare l'assenza di errori critici in casi d'uso semplici. Lo strumento è in grado di costruire ed eseguire test generati e rilevare arresti anomali (segfault), aborti, tutti i tipi di segnali emessi, codice di ritorno del programma diverso da zero e sospensione del programma.

Esempi:


C'è un CUnit

E Embedded Unit è un framework di test unitario per Embedded C System. Il suo design è stato copiato da JUnit e CUnit e altri, e quindi adattato in qualche modo per Embedded C System. L'unità incorporata non richiede le librerie C std. Tutti gli oggetti sono assegnati all'area const.

E Tessy automatizza i test unitari del software incorporato.



Se hai familiarità con JUnit allora ti consiglio CppUnit. http://cppunit.sourceforge.net/cppunit-wiki

Questo presuppone che tu abbia un compilatore c ++ per fare i test unitari. se no, allora sono d'accordo con Adam Rosenfield che controllare è ciò che vuoi.


Non utilizzo un framework, utilizzo solo gli autotools "verifica" il supporto di destinazione. Implementare un "principale" e utilizzare assert (s).

Il mio test dir Makefile.am (s) assomiglia a:

check_PROGRAMS = test_oe_amqp

test_oe_amqp_SOURCES = test_oe_amqp.c
test_oe_amqp_LDADD = -L$(top_builddir)/components/common -loecommon
test_oe_amqp_CFLAGS = -I$(top_srcdir)/components/common -static

TESTS = test_oe_amqp

C'è un elegante framework di test unitario per C con supporto per oggetti finti chiamati cmocka . Richiede solo la libreria C standard, funziona su una gamma di piattaforme di elaborazione (incluso embedded) e con diversi compilatori.

Ha anche il supporto per diversi formati di output dei messaggi come Subunit, Test Anything Protocol e jUnit XML reports.

cmocka è stato creato per funzionare anche su piattaforme embedded e ha anche il supporto di Windows.

Un semplice test assomiglia a questo:

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>

/* A test case that does nothing and succeeds. */
static void null_test_success(void **state) {
    (void) state; /* unused */
}

int main(void) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(null_test_success),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}

L' API è completamente documentata e diversi esempi fanno parte del codice sorgente.

Per iniziare con cmocka dovresti leggere l'articolo su LWN.net: Test delle unità con oggetti mock in C

cmocka 1.0 è stato rilasciato a febbraio 2015.


Un framework di test unitario in C è Check ; una lista di quadri di test unitari in C può essere trovata here ed è riprodotta di seguito. A seconda del numero di funzioni di libreria standard del runtime, è possibile o meno essere in grado di utilizzarne una.

AceUnit

AceUnit (Advanced C e Embedded Unit) si autodefinisce come un comodo framework per test di codice C. Cerca di imitare JUnit 4.x e include capacità di riflessione. AceUnit può essere utilizzato in ambienti con vincoli di risorse, ad esempio lo sviluppo di software embedded e, soprattutto, funziona bene in ambienti in cui non è possibile includere un singolo file di intestazione standard e non può richiamare una singola funzione C standard dalle librerie ANSI / ISO C. Ha anche una porta di Windows. Non usa le forcelle per intrappolare i segnali, anche se gli autori hanno espresso interesse ad aggiungere una funzione del genere. Vedi la homepage di AceUnit .

Autounità GNU

Molto simile a Check, compreso il biforcarsi per eseguire i test unitari in uno spazio indirizzo separato (in effetti, l'autore originale di Check ha preso in prestito l'idea da GNU Autounit). GNU Autounit usa GLib estensivamente, il che significa che il collegamento e tale necessità richiedono opzioni speciali, ma questo potrebbe non essere un grosso problema per te, specialmente se stai già utilizzando GTK o GLib. Vedi la homepage di GNU Autounit .

cunit

Utilizza anche GLib, ma non si biforca per proteggere lo spazio degli indirizzi dei test unitari.

CUnit

Standard C, con piani per un'implementazione della GUI Win32. Al momento non esegue il fork o protegge in altro modo lo spazio degli indirizzi dei test unitari. Nel primo sviluppo. Vedi la homepage di CUnit .

Cutest

Un semplice framework con un solo file .c e un file .h da inserire nell'albero dei sorgenti. Vedi la homepage di CuTest .

CppUnit

Il principale framework di test unitario per C ++; puoi anche usarlo per testare il codice C. È stabile, attivamente sviluppato e ha un'interfaccia GUI. I motivi principali per non usare CppUnit per C sono innanzitutto che è abbastanza grande, e in secondo luogo devi scrivere i tuoi test in C ++, il che significa che hai bisogno di un compilatore C ++. Se questi non sembrano preoccupanti, vale la pena considerare, insieme ad altri framework di test delle unità C ++. Vedi la homepage di CppUnit .

embUnit

embUnit (Embedded Unit) è un'altra unità di test framework per sistemi embedded. Questo sembra essere sostituito da AceUnit. Homepage di Embedded Unit .

MinUnit

Un set minimo di macro e il gioco è fatto! Il punto è mostrare quanto è facile testare il codice dell'unità. Vedi la homepage di MinUnit .

CUnit per Mr. Ando

Un'implementazione di CUnit che è abbastanza nuova e apparentemente ancora in fase di sviluppo iniziale. Vedi la pagina principale di CUnit per Mr. Ando .

Questo elenco è stato aggiornato l'ultima volta nel marzo 2008.

Altri:

CMocka

CMocka è un framework di test per C con supporto per oggetti finti. È facile da usare e configurare. cmocka.org homepage ufficiale.

Criterio

Il criterio è un framework di test delle unità C multipiattaforma che supporta la registrazione automatica dei test, i test parametrizzati, le teorie e che può essere esportato in più formati, tra cui TAP e JUnit XML. Ogni test viene eseguito nel proprio processo, quindi segnali e arresti anomali possono essere segnalati o testati, se necessario. Vedere la pagina iniziale del criterio per ulteriori informazioni.

HWUT

HWUT è uno strumento di unit test generale con un grande supporto per C. Può aiutare a creare Makefile, generare enormi casi di test codificati in "tabelle di iterazione" minime, camminare su macchine a stati, generare C-stub e altro. L'approccio generale è piuttosto unico: i verdetti si basano sul "buon stdout / cattivo stdout". La funzione di confronto, tuttavia, è flessibile. Pertanto, qualsiasi tipo di script può essere utilizzato per il controllo. Può essere applicato a qualsiasi linguaggio in grado di produrre output standard. Vedi la homepage di HWUT .

Wikipedia fornisce un elenco dettagliato dei framework di test dell'unità C sotto Elenco dei framework di test unitari: C


Una tecnica da utilizzare è lo sviluppo del codice di test unitario con un framework C ++ xUnit (e un compilatore C ++), mantenendo l'origine per il sistema di destinazione come moduli C.

Assicurati di compilare regolarmente la tua fonte C sotto il tuo cross-compilatore, se possibile con i tuoi test unitari automaticamente.




Come novellino di C, ho trovato molto utili le diapositive denominate Test driven development in C. Fondamentalmente, usa lo standard assert() insieme a && per consegnare un messaggio, senza alcuna dipendenza esterna. Se qualcuno è abituato a un framework di testing completo, questo probabilmente non lo farà :)


Minunit è un framework di test unitario incredibilmente semplice. Lo sto usando per il codice microcontroller test unit c per avr.



Se sei ancora alla ricerca di framework di test, CUnitWin32 è uno per la piattaforma Win32 / NT.

Questo risolve un problema fondamentale che ho dovuto affrontare con altri framework di test. In altre parole, le variabili globali / statiche sono in uno stato deterministico perché ogni test viene eseguito come processo separato.



Non c'è davvero niente di sbagliato in questo, è solo una questione se si chiama un test unitario o un test di integrazione. Devi solo assicurarti che se interagisci con il file system, non ci sono effetti collaterali non voluti. In particolare, assicurati di ripulire tutto da solo - cancella tutti i file temporanei che hai creato - e di non sovrascrivere accidentalmente un file esistente che ha lo stesso nome di un file temporaneo che stavi utilizzando. Usa sempre percorsi relativi e non percorsi assoluti.

Sarebbe anche una buona idea fare chdir() in una directory temporanea prima di eseguire il test, e chdir() in seguito.







c unit-testing testing embedded