Come deve essere organizzato il codice di test dell'unità C ++ per l'efficienza massima dell'unità?

45
  • Questa domanda è non su Unit Testing Frameworks.
  • Questa domanda è non sulla scrittura dei Test di unità.
  • Questa domanda riguarda dove inserire il codice UT scritto e come / quando / dove compilarlo ed eseguirlo.

In Funzionante in modo efficace con il codice legacy , Michael Feathers afferma che

good unit tests ... run fast

e quello

A unit test that takes 1/10th of a second to run is a slow unit test.

Penso che queste definizioni abbiano senso. Penso anche che implichino che devi mantenere un insieme di Test unitari e un insieme di Quei test di codice che richiedono più tempo separatamente, ma suppongo che sia il il prezzo che si paga per chiamare solo un Test unitario se viene eseguito (molto) velocemente.

Ovviamente il problema in C ++ è quello di "eseguire" il tuo Test di unità ( s ), devi:

  1. Modifica il tuo codice (produzione o Unit Test, a seconda del "ciclo" in cui ti trovi)
  2. Compila
  3. Collegamento
  4. Start Unit Test Executable ( s )

Modifica (dopo uno strano voto ravvicinato) : prima di entrare nei dettagli, proverò a riassumere il punto qui:

In che modo il codice di test dell'unità C ++ può essere organizzato in modo efficace, in modo che sia efficiente modificare il codice (test) e eseguire il codice di test?

Il primo problema è quindi decidere dove inserire il codice del test unitario in modo che:

  • è "naturale" modificarlo e visualizzarlo in combinazione con il codice di produzione associato.
  • è facile / veloce avviare il ciclo di compilazione per l'unità in corso di modifica

Il secondo , problema correlato, è quindi cosa compilare in modo che il feedback sia istantaneo.

Opzioni estreme:

  • Ogni Unit-Test-Test-Unit risiede in un file cpp separato e questo file cpp viene compilato + collegato separatamente (insieme al file dell'unità codice sorgente sottoposto a test) su un singolo eseguibile che esegue quindi questo Test unità.
    • (+) Questo riduce al minimo il tempo di avvio (compilazione + collegamento!) per la singola unità di test.
    • (+) Il test viene eseguito super veloce, perché verifica solo un'unità.
    • (-) L'esecuzione dell'intera suite richiederà l'avvio di un migliaio di processi. Può essere un problema da gestire.
    • (-) Il sovraccarico delle partenze del processo diventerà visibile
  • L'altra parte dovrebbe avere - ancora - un file cpp per test, ma tutti i file test cpp (insieme al codice che testano!) sono collegati in un eseguibile (per modulo / per progetto / scegli il tuo scelta).
    • (+) Il tempo di compilazione sarebbe ancora OK, poiché verrà compilato solo il codice modificato.
    • (+) L'esecuzione dell'intera suite è semplice, in quanto esiste un solo exe da eseguire.
    • (-) La suite impiegherà anni per collegarsi, poiché ogni ricompilazione di qualsiasi oggetto attiverà un nuovo collegamento.
    • (-) (?) Il seme impiegherà più tempo per essere eseguito, anche se se tutti i test unitari sono veloci, l'ora dovrebbe essere OK.

Quindi, come vengono gestiti i test C ++ delle unità del mondo reale? Se eseguo solo quella roba ogni notte / ora, la seconda parte non ha molta importanza, ma la prima parte, vale a dire come "accoppiare" il codice UT al codice di produzione, in modo che sia "naturale" per gli sviluppatori mantenere entrambi in la messa a fuoco conta sempre, penso. (E se gli sviluppatori hanno a fuoco il codice UT, vorranno eseguirlo, il che ci riporta alla seconda parte.)

Storie del mondo reale ed esperienza apprezzate!

Note:

  • Questa domanda lascia intenzionalmente la piattaforma non specificata e il sistema make / project.
  • Domande con tag UT & C ++ è un ottimo punto di partenza, ma purtroppo troppe domande, e soprattutto le risposte, sono troppo focalizzate sui dettagli o su framework specifici.
  • Qualche tempo fa, ho risposto a domanda simile sulla struttura per i test dell'unità di spinta. Trovo che questa struttura sia carente per test di unità "reali" veloci. E trovo l'altra domanda troppo stretta, quindi questa nuova domanda.
posta Martin Ba 31.08.2011 - 13:25
fonte

2 risposte

6

Abbiamo tutti i test unitari (per un modulo) in un eseguibile. I test sono messi in gruppi. Posso eseguire un singolo test (o alcuni test) o un gruppo di test specificando un nome (test / gruppo) sulla riga di comando del test runner. Il sistema di build può eseguire il gruppo "Build", il reparto di test può eseguire "Tutti". Lo sviluppatore può inserire alcuni test in un gruppo come "BUG1234" con 1234 come numero di tracciamento del problema su cui sta lavorando.

    
risposta data 31.08.2011 - 13:38
fonte
6

Per prima cosa, non sono d'accordo con "1) Modifica il tuo codice (di produzione) e il tuo Test unitario". Dovresti modificare solo uno alla volta, altrimenti se il risultato cambia non saprai quale lo ha causato.

Mi piace mettere i test unitari in un albero delle directory che ombreggia l'albero principale. Se ho /sources/componentA/alpha/foo.cc e /objects/componentA/beta/foo.o , allora voglio qualcosa come /UTest_sources/componentA/alpha/test_foo.cc e /UTest_objects/componentA/beta/test_foo.o . Io uso lo stesso albero delle ombre per gli oggetti stub / mock e qualsiasi altra fonte abbia bisogno dei test. Ci saranno alcuni casi limite, ma questo schema semplifica molto le cose. Una buona macro di editor può richiamare la fonte di test accanto alla fonte dell'oggetto senza sforzo. Un buon sistema di compilazione (ad es. GNUMake) può compilare entrambi ed eseguire il test con un comando (ad esempio make test_foo ), e può gestire un tale bazillion processi - solo quelli le cui origini sono cambiate dall'ultima prova - abbastanza facilmente (Non ho mai trovato il sovraccarico di avviare questi processi per essere un problema, è O (N)).

Nello stesso framework, è possibile avere test su scala più ampia (non più test unitari) che collegano molti oggetti insieme ed eseguono molti test. Il trucco consiste nell'ordinare questi test per quanto tempo impiegano a costruire / eseguire, e lavorarli di conseguenza nel tuo programma giornaliero. Esegui il test di un secondo o meno ogni volta che ne hai voglia; iniziare il test di dieci secondi e allungare; test di cinque minuti e fare una pausa; prova di mezz'ora e andare a pranzo; prova di sei ore e vai a casa. Se trovi che stai sprecando un sacco di tempo, ad es. ricollegando un test enorme dopo aver modificato un solo file piccolo, lo stai facendo male, anche se il collegamento fosse istantaneo, avresti comunque eseguito un lungo test quando non è stato richiesto.

    
risposta data 31.08.2011 - 18:43
fonte

Leggi altre domande sui tag