2008-09-23 17 views
6

C'è un sacco di gente oggi che vendono unit testing come il pane e burro di sviluppo. Potrebbe funzionare anche per routine fortemente orientate all'algoritmo. Tuttavia, come testare l'unità, ad esempio, un allocatore di memoria (si pensi malloc()/realloc()/free()). Non è difficile produrre un allocatore di memoria funzionante (ma assolutamente inutile) che soddisfi l'interfaccia specificata. Ma come fornire il contesto appropriato per la funzionalità di unit test che è assolutamente voluta, ma non fa parte del contratto: coalescenza blocchi liberi, riutilizzare blocchi liberi sulla prossimi allocazioni, tornando memoria libera in eccesso al sistema, affermando che la politica di assegnazione (ad esempio, first-fit) in realtà è rispettato, eccCome si svita un allocatore di memoria?

mia esperienza è che le affermazioni, anche se complessa e richiede molto tempo (ad esempio, attraversando l'intera lista gratuita per controllare invarianti) sono molto meno lavoro e sono più affidabili di unit test , esp. quando si codificano algoritmi complessi e dipendenti dal tempo.

Qualche idea?

risposta

10

Il codice altamente testabile tende a essere strutturato in modo diverso rispetto ad altri codici.

Si descrivono diverse attività che si desidera un allocatore da fare:

  • coalescenza blocchi liberi
  • riutilizzare blocchi liberi sulla prossimi allocazioni
  • ritorno memoria libera in eccesso al sistema
  • asserire che la politica di allocazione (ad es. first-fit) sia realmente rispettata

Mentre è possibile scrivere il codice di allocazione in modo che sia molto accoppiato, come nel fare alcune di quelle cose all'interno di un corpo di una funzione, è anche possibile suddividere ciascuna attività in codice che è un blocco verificabile. Questa è quasi un'inversione di ciò a cui potresti essere abituato. Trovo che il codice testabile tende ad essere molto trasparente e costruito da più piccoli pezzi.

Avanti, direi è che entro la ragione test automatizzati di qualsiasi tipo, è meglio che non test automatizzati. Mi concentrerei sicuramente di più per assicurarmi che i tuoi test facessero qualcosa di utile che preoccuparti se hai usato correttamente i mock, se hai assicurato che fosse correttamente isolato e se fosse un vero test unitario. Questi sono tutti obiettivi ammirevoli che si spera possano migliorare il 99% dei test. D'altra parte, per favore usa il buon senso e il tuo miglior giudizio ingegneristico per portare a termine il lavoro.

Senza esempi di codice, non penso di poter essere più specifico.

1

Entrambe le cose hanno il loro posto. Utilizzare i test unitari per verificare che le interfacce si comportino come previsto e le asserzioni per verificare che il contratto sia rispettato.

9

Se c'è una logica in là, può essere unità testate.
Se la logica comporta prendere decisioni e chiamando le API OS/hardware/sistema, falso/deridere il dispositivo chiamate a carico e unità di prova la vostra logica per verificare se le decisioni giuste siano legati a un dato insieme di pre-condizioni. Segui la triade Arrange-Act-Assert nel tuo test unitario.
Le asserzioni non sostituiscono i test unitari automatizzati. Non ti dicono quale scenario ha fallito, non forniscono feedback durante lo sviluppo, non possono essere utilizzati per dimostrare che tutte le specifiche sono soddisfatte dal codice, tra le altre cose.

non vaga Aggiornamento: Non so il metodo esatto chiama .. Penso che 'rotolare il mio' Diciamo che il codice prende in esame le condizioni attuali, prende una decisione e fare chiamate al Sistema operativo come richiesto. Diciamo che le chiamate del sistema operativo sono (si può avere molti di più):

void* AllocateMemory(int size); 
bool FreeMemory(void* handle); 
int MemoryAvailable(); 

Prima trasformare questo in un'interfaccia, I_OS_MemoryFacade. Creare un'implementazione di questa interfaccia per effettuare le chiamate effettive al sistema operativo. Ora fai in modo che il tuo codice utilizzi questa interfaccia: ora hai disaccoppiato il tuo codice/logica dal dispositivo/sistema operativo. Successivamente nel test dell'unità, si utilizza un framework fittizio (il suo scopo è quello di fornire un'implementazione simulata di un'interfaccia specificata. È quindi possibile dire al framework mock che si aspetta queste chiamate da effettuare, con questi parametri e restituirlo quando Alla fine del test, puoi chiedere al framework finto di verificare se tutte le aspettative sono state soddisfatte (es. In questo test, AllocateMemory dovrebbe essere chiamato tre volte con 10, 30, 50 come parametri seguiti da 3 chiamate FreeMemory . Controllare se MemoryAvailable restituisce il valore iniziale.)
Dal momento che il codice dipende un'interfaccia, che non conosce la differenza tra il reale attuazione e implementazione un falso/finta che si utilizza per il test. Google out 'quadri finto' per ulteriori informazioni su questo.

+0

Potrebbe per favore fornire maggiori dettagli su come scrivere i test unitari. Li stai difendendo molto, ma ho letto la domanda e poi la tua risposta e ancora non capisco cosa stai proponendo esattamente. –

+0

La tua risposta è troppo astratta per essere utile. Puoi dare un esempio di come testare un allocatore di memoria? Rendiamolo ancora più concreto: first-fit, che restituisce il 50% dei blocchi liberi al sistema quando c'è il doppio di spazio disponibile rispetto alla memoria allocata. – zvrba

+0

Il primo passo consiste nell'ottenere l'allocatore di memoria e rilasciare memoria dal sistema tramite un'interfaccia e non direttamente tramite chiamate di sistema dirette. Il secondo passaggio consiste nel creare una versione fittizia di questa interfaccia che è possibile utilizzare nei test. Il terzo passaggio consiste nel testare e verificare come il codice utilizza l'interfaccia. –

0

Personalmente trovo la maggior parte dei test unitari come il desiderio di qualcun altro piuttosto che il mio. Penso che ogni unit test dovrebbe essere scritto come un normale programma eccetto il fatto che non fa altro che testare una libreria/algoritmo o qualsiasi parte di codice.

I miei test di unità di solito non utilizzano strumenti come CUnit, CppUnit e software simile. Creo i miei test.Per esempio non molto tempo fa avevo bisogno di testare una nuova implementazione di un contenitore nei casi normali per perdite di memoria. Un test unitario non è stato abbastanza utile per fornire un buon test. Invece ho creato il mio allocatore personale e ho reso impossibile allocare memoria dopo un certo numero (regolabile) di allocazioni per vedere se la mia applicazione ha perdite di memoria in questi casi (e aveva :)).

Come si può fare con un test unitario? Con più impegno per rendere il codice adatto al "modello" di test unitario.

quindi vi consiglio vivamente di non utilizzare unit test ogni volta solo perché è "trendy", ma solo quando è veramente facile da integrarli con il codice che ti piace mettere alla prova.

1

Si potrebbe anche voler includere test delle prestazioni, test di stress ecc. Non sarebbero test unitari, perché testare l'intera cosa, ma sono molto preziosi in caso di allocatore di memoria.

test delle unità non esclude questo tipo di test. È meglio averli entrambi.

1

Penso anche che i test unitari siano sopravvalutati. Hanno la loro utilità, ma ciò che realmente aumenta la qualità di un programma è di rivederlo. D'altra parte sono molto affezionato alle asserzioni, ma non sostituiscono i test unitari.

Non sto parlando di peer-review, ma semplicemente rileggo quello che hai scritto, possibilmente mentre lo attraversi con un debugger e controllando che ogni linea faccia quello che dovrebbe fare la qualità del software sky-rocket.

mi consiglia di test di unità "alto livello" che mettono alla prova un pezzo di funzionalità piuttosto che un metodo molto piccola chiamata. Più tardi tendono a rendere ogni cambiamento di codice estremamente doloroso e costoso.

0

Il test dell'unità non è solo per assicurarsi che il codice funzioni. È anche un'ottima metodologia di progettazione. Affinché i test siano utili, come accennato in precedenza, il codice deve essere il più possibile disaccoppiato, ad esempio utilizzando le interfacce laddove necessario.

Non sempre scrivo prima i test, ma molto spesso se ho difficoltà a iniziare qualcosa, scriverò un semplice test, sperimenterò il design e andrò da lì. Inoltre, i buoni test unitari servono come buona documentazione. Al lavoro quando ho bisogno di vedere come usare una classe specifica o simili, guardo i test unitari per questo.

Basta ricordare che Unit Testing è non test di integrazione. Il test unitario ha i suoi limiti ma, nel complesso, ritengo che sia un ottimo strumento per sapere come usare in modo appropriato.

0

Quindi si verifica un problema che l'allocatore viene utilizzato dal framework di test che potrebbe causare problemi per lo stato del proprio allocatore mentre si sta tentando di eseguire il test. Prendi in considerazione il prefisso delle funzioni del tuo allocatore (vedi dlmalloc). Si scrive

prefix_malloc(); 
prefix_free(); 

e poi

#ifndef USE_PREFIX 
#define prefix_malloc malloc 
#define prefix_free free 
#endif 

Ora, impostare il sistema di compilazione per compilare una versione della libreria utilizzando -DUSE_PREFIX. Scrivi i tuoi test unitari per chiamare prefix_malloc e prefix_free. Ciò ti consente di separare lo stato del tuo allocatore dallo stato dell'allocatore di sistema.

Se si utilizza sbrk e l'allocatore di sistema utilizza sbrk è possibile che si possa avere un momento negativo se uno degli allocatori presuppone che abbia il controllo completo sul punto di interruzione. In questo caso si vorrebbe collegare in un altro allocatore che è possibile configurare per utilizzare solo mmap in modo che l'allocatore possa avere il punto di interruzione.

Problemi correlati