2010-02-26 9 views
18

Sono uno sviluppatore di software abbastanza nuovo che attualmente sta lavorando aggiungendo i test unitari a un progetto C++ esistente iniziato anni fa. A causa di un motivo non tecnico, non sono autorizzato a modificare alcun codice esistente. La classe base di tutti i miei moduli ha un sacco di metodi per impostare/ottenere dati e comunicare con altri moduli.Mocking di metodi non virtuali in C++ senza modificare il codice di produzione?

Dato che voglio testare ogni singolo modulo, voglio essere in grado di utilizzare valori predefiniti per tutti i miei metodi di comunicazione tra moduli. Cioè per un metodo Ping() che controlla se un altro modulo è attivo, voglio averlo restituito vero o falso in base al tipo di test che sto facendo. Ho esaminato Google Test e Google Mock e supporta metodi di simulazione non virtuali. Tuttavia l'approccio descritto (http://code.google.com/p/googlemock/wiki/CookBook#Mocking_Nonvirtual_Methods) mi obbliga a "templatizzare" i metodi originali per prendere oggetti reali o fittizi. Non posso andare a templatizzare i miei metodi nella classe base a causa del requisito menzionato prima, quindi ho bisogno di un altro modo di deridere questi metodi virtuali

Fondamentalmente, i metodi che voglio prendere in giro sono in qualche classe base, il I moduli che voglio testare e creare mock di sono classi derivate di quella classe base. Ci sono moduli intermedi tra la mia classe del modulo base e i moduli che voglio testare.

Gradirei qualsiasi consiglio!

Grazie,

JW

EDIT: A esempi più concreti

La mia classe di base è diciamo rootModule, il modulo che voglio testare è leafModule. Esiste un modulo intermedio che eredita da rootModule, leafModule eredita da questo modulo intermedio.

Nel mio leafModule, voglio testare il metodo doStuff(), che chiama il GetStatus (moduleName) non virtuale definito nella classe rootModule. Devo in qualche modo rendere GetStatus() per restituire un valore predefinito scelto. Il derisione è nuovo per me, quindi usare gli oggetti finti anche l'approccio giusto?

+0

È possibile modificare localmente il codice solo per i test? Non dovresti effettuare il check-in delle modifiche. –

+0

È necessario utilizzare i test unitari? Esistono altri metodi (automatizzati) di test di regressione. Il test unitario è uno strumento, non una religione - se non si adatta, non usarlo. – ima

+0

Idealmente il mio team vorrebbe utilizzare i test unitari, visto che il nostro albero dei moduli è piuttosto grande, vorremmo davvero disaccoppiare il testing delle funzionalità tra moduli dal test dei metodi locali di un modulo. È spiacevole però che le nostre precedenti decisioni di progettazione abbiano reso le prove unitarie un po 'una sfida. A quali altri metodi di test di regressione ti stai riferendo? Immagino che, usando altre metodologie di test, avremo ancora bisogno di una sorta di metodo per impostare i metodi non virtuali o restituire valori predefiniti, il che significa che troveremo una soluzione alternativa o manterremo un altro albero dei sorgenti intorno. – wk1989

risposta

3

Scriverò uno script Perl/Ruby/Python per leggere nell'albero di origine originale e scrivere un albero dei sorgenti di mocking in una directory diversa. Non è necessario analizzare completamente C++ per sostituire una definizione di funzione.

+3

Probabilmente funzionerebbe, tuttavia sarebbe più semplice IMO avere solo 2 alberi sorgente e in uno di essi cambiare i metodi della classe base per essere virtuali. Ci sarebbero alcuni aspetti negativi che riguardano i ragazzi che sono responsabili del sistema di controllo della sorgente. Gli sviluppatori dovranno anche adattarsi alla scrittura del codice per 2 revisioni di lavoro. Quello che sto cercando è una soluzione che mi permetta di integrare i miei test unitari in un singolo albero di sorgenti che può essere costruito (ed eseguito) in un unico passaggio. Quello che non sono sicuro è se questo è possibile. – wk1989

+1

@ wk1989: gli sviluppatori non lavorerebbero mai su due alberi di origine con l'idea di script. Questo è il punto. Tutta la modifica del codice verrebbe eseguita nell'albero del codice reale. I test verrebbero eliminati dall'albero di test generato dallo script. –

1

Un approccio sarebbe quello di specificare diverse fonti per il test. Supponiamo che il target di produzione utilizzi rootModule.h e rootModule.cpp. Utilizza diverse fonti per il tuo obiettivo di test. È possibile specificare un'intestazione diversa modificando il percorso di inclusione, in modo che #include "rootModule.h" carichi effettivamente unittest/rootModule.h. Quindi metti root rootModule sul contenuto del tuo cuore.

12

Esistono diversi modi per sostituire le funzioni non virtuali. Uno è di ri-dichiararli e compilare un nuovo eseguibile di test per ciascuna serie diversa di funzioni non virtuali che desideri testare. Questo è difficilmente scalabile.

Una seconda opzione è renderle virtuali per il test. La maggior parte dei compilatori ti consente di definire qualcosa sulla riga di comando in modo da compilare il tuo codice con -DTEST_VIRTUAL = virtual o -DTEST_VIRTUAL per renderli virtuali o normali a seconda che siano o meno sottoposti a test o meno.

Una terza opzione che può essere utilizzabile consiste nell'utilizzare una struttura di simulazione che consente di simulare funzioni non virtuali. Sono l'autore di HippoMocks (disclaimer per quanto riguarda la neutralità e così via) e di recente abbiamo aggiunto la possibilità di fingere semplici funzioni C su piattaforme X86.Questo può essere esteso a funzioni membro non virtuali con un po 'di lavoro e sarebbe quello che stai cercando. Tieni presente che, se il tuo compilatore può vedere contemporaneamente l'uso e la definizione di una funzione, potrebbe essere in linea e che il mocking potrebbe non riuscire. Ciò vale in particolare per le funzioni definite nelle intestazioni.

Se la funzione C normale per te è sufficiente, puoi utilizzarla come è ora.

+1

Ho appena trovato questa risposta. Ho intenzione di utilizzare HippoMocks per un po 'di tempo (la prima volta che uso un framework di simulazione), ma mi sto propendendo per l'integrazione delle dipendenze in fase di compilazione usando i template, e non ero sicuro che HippoMocks potesse gestirlo. Puoi spiegare un po 'di più cosa sarebbe necessario per simulare le funzioni dei membri non virtuali? E perché l'inganno potrebbe causare il fallimento della derisione? – JBentley

+0

Le funzioni membro non virtuali possono essere incorporate dal compilatore durante la compilazione di un'unità o dal linker al momento del collegamento (LTO). Entrambi significano che l '"indirizzo" di una funzione non è più ben definito, quindi sostituirlo con qualcos'altro è anche impossibile in quanto non è possibile trovare quelle altre "nuove" posizioni. Per le funzioni dei membri virtuali è garantito che ciò non avvenga (dato che sono virtuali, non è possibile eseguire l'inlining). – dascandy

+0

Le funzioni non virtuali vengono prese in giro sostituendo i primi N byte della funzione - letteralmente hackerando il codice in fase di esecuzione - con un salto a una funzione fittizia. Funziona solo se c'è una sola posizione della funzione da modificare; non puoi avere un puntatore a funzione in più posizioni. La risposta è suddivisa in due commens mentre sono a lunghezza limitata. – dascandy

Problemi correlati