2009-09-17 12 views
15

Non so come voi ragazzi testare il codice ogni volta che si codifica un po 'e per i diversi livelli di prova: test di unità, di integrazione, ...testare il codice in C++ C

Ad esempio, per l'unità testare una funzione che hai appena scritto, scrivi un altro insieme di funzioni principali e Makefile per testarlo? O modifichi la funzione principale del tuo progetto per testare la funzione. Oppure esegui il tuo progetto sotto debug, e fermati dove la funzione sta per essere chiamata e modifica i valori dei suoi argomenti?

Credo che ci debbano essere alcuni modi convenienti e comuni che la maggior parte delle persone utilizza e di cui non sono a conoscenza.

risposta

3

xUnit è una famiglia di moduli di test dell'unità. x è sostituito da una lettera per il linguaggio del framework usato. La famiglia è attualmente composto da:

Ho lavorato in progetti utilizzando CppUnit con buoni risultati. Recentemente ho provato ad integrarlo in un ambiente di compilazione automatico (ad esempio Hudson) e ho incontrato molti ostacoli.

Idealmente, la compilazione crea automaticamente e esegue i test unitari. In tal caso, il codice viene eseguito dall'ambiente di test (e quindi ha il proprio ciclo principale). Una complicazione extra nel mio caso è che lavoro con i sistemi embedded; printf non è sempre possibile. Mi aspetto che se si esegue su PC, CUnit e CppUnit possono aiutarvi molto ad implementare un buon test unitario. Si prega di guardare come utilizzare i risultati; un sistema di integrazione continua aumenterà molto il tuo rendimento.

Un altro quadro che vale la pena di dare un'occhiata è Maestra. Si basa su C99 (che Microsoft non ha mai implementato, ma per gcc è fantastico!)

4
+1

GoogleTest è un bel pacchetto facile da usare. È anche ben documentato. L'unica ragione per cui sono passato a cpptest è perché i macro di google sono così complessi da causare errori nell'analisi e nel completamento del mio IDE. Non è colpa di googletest, ma è ancora un problema. – Jay

2

Io di solito fare in questo modo:

int foo(int bar) { 
    ... 
} 

#ifdef FOO_UNITTEST 
int main(int argc, char *argv[]) { 
    // tests 
} 
#endif 

e ho un makefile che ha -DFOO_UNITTEST in CFLAGS.

È goffo ma i test sono sempre a portata di mano accanto al codice.

+0

+1 Buona dimostrazione di un test unitario di base. –

1

Si dovrebbe anche considerare di eseguire uno strumento di copertura del test per vedere se i test effettivamente esercitano una quantità sufficiente di codice per essere una buona serie di test. Vedi SD Test Coverage for C.

0

In genere inizio con qualcosa di così complicato come MinUnit quindi creare un set di macro adatto al progetto su cui sto lavorando per soddisfare le convenzioni di quel progetto.

Ogni componente avrà un file eseguibile che crea ed esegue i test di unità per quel componente.

Non utilizzo un debugger in quanto richiede l'intervento umano, quindi non è possibile utilizzarlo per la regressione automatica. Non modifico l'eseguibile principale, dato che di solito coinvolge molti componenti ed è abbastanza complicato senza avere molti macro per accendere e spegnere i bit. Creare un nuovo target usando make è molto semplice.

9

L'approccio di Test Driven Development (TDD) è di scrivere per prima cosa il test, vedere che fallisce di default (cioè, un nuovo il test ha esito positivo fallendo), quindi scrivere la funzione per superare il test.

In questo modo, il test non diventa un ripensamento, ma il cuore della metodologia di sviluppo.

Perché si può essere lo sviluppo di una funzione (metodo) prima di aver implementato gli oggetti su cui si opera, la maggior parte dei quadri TDD forniscono anche un impianto per la generazione di oggetti "finti", che restituirà risultati attesi prima che le loro classi sono effettivamente applicate .

Io personalmente raccomando Google prova e Google Mock:

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

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

1

amo UnitTest++. È davvero facile da configurare e la scrittura dei test è più semplice rispetto al tipico CppUnit preferito. Ogni test ha pochissime spese di programmazione, quindi è più probabile che tu li scriva!

Per quanto riguarda ciò che provo, tendo a testare le interfacce pubbliche delle classi, ma se sto solo rifacendo e suddividendo le cose in funzioni più piccole, non scriverò una serie di test per ciascuna di queste funzioni - dovrebbero essere coperti dai test che testano l'interfaccia pubblica. Finché l'interfaccia pubblica funziona secondo i test, tutto va bene nel mondo.

3

Ho imparato ad amare googletest/googlemock. Sono una combinazione molto potente e facile da usare. C'è anche molta documentazione disponibile sulle loro pagine wiki.

googletest: code.google.com/p/googletest/wiki/GoogleTestPrimer

googlemock: code.google.com/p/googlemock/wiki/ForDummies

2

amo googletest e googlemock. Davvero facile da configurare e ben documentato, here c'è una buona introduzione al framework.

1

Io uso boost.test. Mi sono trasferito da cppunit, che era buono ma troppo java, questo era un problema perché junit utilizza il reflection per trovare ed eseguire i test, poiché non è disponibile in C++ devi usare macro per registrare i tuoi test, in cppunit tu devi dichiarare, registrare e definire i tuoi test in tre diversi posti. boost.test ti permette di dichiarare, registrare e definire il tuo test in una dichiarazione, questo è molto bello.

Il mio approccio generale è il nuovo codice TDD e cerco di utilizzare il test dell'unità per codice legacy. Sto testando qualsiasi codice diverso su piattaforme diverse per garantire che si comportino allo stesso modo e continuino a comportarsi allo stesso modo.

I struttura i miei progetti in modo che ogni libreria o progetto eseguibile disponga anche di un progetto di test unitario. Per i test eseguibili, includo i file di origine eseguibili (eccetto main) nel progetto di test e aggiungo il mio test in nuovi file. Per i test di libreria, di solito collego semplicemente la libreria eccetto quando sto testando parti private di DLL, quindi utilizzo l'approccio eseguibile.

L'utilizzo di CMake consente di astrarre eventuali duplicazioni tra il progetto di origine e il progetto di test. Inoltre, CTest si integra perfettamente con qualsiasi framework di testing dell'unità che esegue il test dei pacchetti in file eseguibili. Ciò consente di eseguire tutti gli eseguibili di test in una soluzione in un sol colpo e riporta un riepilogo dei risultati. Inoltre si integra con un framework di integrazione continuo chiamato CDash.

Una nota su TDD, molte persone considerano questo come uno sviluppo Test driven, ma può essere il Test Driven Design. Questo è un ottimo modo per concentrarsi su un design agile, usare TDD per progettare il mio software e scrivere mi ha davvero aperto gli occhi.

7

Confronto tra CppTest e CppUnit Vorrei andare con CppTest. CppTest ha meno strutture nascoste e IMO più facili da capire e implementare. Mi piace personalmente vedere il punto di ingresso principale . Ho anche incluso Boost Unit Testing Framework. Non è basato su xUnit. Non sono un fan, ma se stai già usando la libreria Boost sarebbe bello da incorporare.


CppTest vs CppUnit

facilità di creazione di uno unit test e il test Suite. Sia CppUnit che CppTest creano i test unitari dei metodi di classe, con la classe derivata da alcune classi di test fornite dallo strumento . La sintassi per CppTest è leggermente più semplice, , con la registrazione di prova eseguita all'interno del costruttore classe . Nel caso di CppUnit, sono necessarie le macro aggiuntive CPPUNIT_TEST_SUITE e CPPUNIT_TEST_SUITE_ENDS.

Esecuzione dei test. CppTest semplicemente chiama il metodo run sulla suite di test, mentre CppUnit utilizza una classe TestRunner separata il cui metodo di esecuzione è richiamato per l'esecuzione dei test.

Estensione della gerarchia di test. In il caso di CppTest, è sempre possibile estendere il precedente test suite creando una nuova classe che eredita da precedente. La nuova classe definirà alcune funzioni aggiuntive che si aggiungono al lotto di prova dell'unità. È sufficiente richiamare il metodo di esecuzione sull'oggetto del nuovo tipo di classe. CppUnit, al contrario, richiede che si usi la macro CPPUNIT_TEST_SUB_SUITE insieme all'ereditarietà della classe per ottenere lo stesso effetto .

Generazione dell'output formattato. Entrambi CppTest e CppUnit hanno l'abilità per personalizzare l'output. Tuttavia, sebbene CppTest abbia un utile, formattatore di output HTML predefinito , CppUnit no. Tuttavia, CppUnit supporta esclusivamente la formattazione XML. Entrambi supportano il testo e lo stile del compilatore formati.

Creazione di dispositivi di prova. Per utilizzare i dispositivi di prova , CppUnit richiede che la classe di prova sia derivata da CppUnit :: TestFixture. È necessario fornire le definizioni per l'installazione e le routine di rimozione . Nel caso di CppTest, è necessario fornire le definizioni solo per l'installazione e le routine di rimozione di . Questa è sicuramente la una soluzione migliore, in quanto mantiene semplice il codice client . • Supporto macro utility predefinito . Entrambi CppTest e CppUnit dispongono di un insieme analogo di macro per asserzioni, gestione di float, e così via.

File di intestazione. CppTest richiede che includa un singolo file di intestazione, mentre il codice client CppUnit deve includere intestazioni multiple , come HelperMacros.h e TextTestRunner.h, a seconda delle funzioni utilizzate.

http://www.ibm.com/developerworks/aix/library/au-ctools3_ccptest/index.html?ca=drs-


CPPTEST

#include “cppTest.h” 

class myTestWithFixtures : public Test::Suite { 
    void function1_to_test_some_code(); 
    void function2_to_test_some_code(); 

    public: 
    myTestWithFixtures () { 
     TEST_ADD (function1_to_test_some_code) {...}; 
     TEST_ADD (function2_to_test_some_code) {...}; 
    } 

    protected: 
    virtual void setup() { ... }; 
    virtual void tear_down() { ... }; 
}; 

int main () 
{ 
    myTestWithFixtures tests; 
    Test::TextOutput output(Test::TextOutput::Verbose); 
    return tests.run(output); 
} 

http://www.ibm.com/developerworks/aix/library/au-ctools3_ccptest/index.html?ca=drs-


CppUnit

#include <cppunit/extensions/TestFactoryRegistry.h> 
#include <cppunit/ui/text/TextTestRunner.h> 
#include <cppunit/extensions/HelperMacros.h> 

class mystringTest : public CppUnit::TestFixture { 
public: 
    void setUp() { ... }; 
    void tearDown() { ... }; 

    void function1_to_test_some_code() { ... }; 
    void function2_to_test_some_code() { ... }; 

    CPPUNIT_TEST_SUITE(mystringTest); 
    CPPUNIT_TEST(function1_to_test_some_code); 
    CPPUNIT_TEST(function2_to_test_some_code); 
    CPPUNIT_TEST_SUITE_END(); 
}; 
CPPUNIT_TEST_SUITE_REGISTRATION(mystringTest); 

con le macro fuori

int main() 
{ 
    CppUnit::TestSuite* suite = new CppUnit::TestSuite("mystringTest"); 
    suite->addTest(new CppUnit::TestCaller<mystringTest>("checkLength", 
       &mystringTest::checkLength)); 
    suite->addTest(new CppUnit::TestCaller<mystringTest>("checkValue", 
       &mystringTest::checkLength)); 

    // client code follows next 
    CppUnit::TextTestRunner runner; 
    runner.addTest(suite); 

    runner.run(); 
    return 0; 
} 

http://www.ibm.com/developerworks/aix/library/au-ctools2_cppunit/


Boost Unit Testing Framework

#include <boost/test/unit_test.hpp> 

using namespace std; 

struct CMyFooTestFixture 
{ 
    CMyFooTestFixture() { ... } //SetUp 
    ~CMyFooTestFixture() { ... } //TearDown 

    void function1_to_test_some_code(CMyFoo& foo) { ... }; 
    void function2_to_test_some_code(CMyFoo& foo) { ... }; 
} 

BOOST_FIXTURE_TEST_SUITE(MyFooTest, CMyFooTestFixture); 

BOOST_AUTO_TEST_CASE(function1_to_test_some_code) 
{ 
    CMyFoo foo; 
    function1_to_test_some_code(foo); 
} 

BOOST_AUTO_TEST_CASE(function1_to_test_some_code2) 
{ 
    CMyFoo foo; 
    function1_to_test_some_code(foo); 
} 

BOOST_AUTO_TEST_SUITE_END(); 

http://www.beroux.com/english/articles/boost_unit_testing/