2009-05-13 12 views
5

Ho visto molti metodi pazzi per ottenere l'accesso a variabili private durante il test dell'unità. Il più strabiliante che abbia mai visto è #define private public.Test unitario con -fno-access-control

Tuttavia, non ho mai visto nessuno suggerire di disattivare le variabili private a livello di compilatore. Ho sempre pensato che non potessi. Mi sono lamentato con molti sviluppatori che i test di unità sarebbero stati molto più semplici se potessi dire al compilatore di fare marcia indietro per questo unico file.

Quindi inciampo sull'opzione del compilatore gcc -fno-access-control. È ovviamente il modo perfetto per testare le unità. I tuoi file sorgente originali non sono modificati, nessun amico iniettato solo per il test unitario, nessuna ricompilazione con bizzarre magie del preprocessore. Basta sfiorare l'interruttore 'nessun controllo accessi' durante la compilazione dei test dell'unità.

mi sto perdendo qualcosa? E 'questa l'unità che prova il proiettile d'argento spero che sia?

L'unico svantaggio che vedo è la natura specifica di gcc della tecnica. Tuttavia, suppongo che MSVS abbia una bandiera simile.

+0

Hai provato questa tecnica? Funziona davvero? – UncleZeiv

+0

Ho compilato un semplice test case prima di pubblicare la domanda. Funziona davvero. Non l'ho provato con una vita reale o un progetto di grandi dimensioni, ma presumo che si adatti perfettamente. –

+0

'-fno-access-control' non funziona con ereditarietà privata/protetta. Sono bloccato con '# define's. – Xeverous

risposta

6

direi che unit test non dovrebbe essere necessario l'accesso ai membri privati.

In generale, i test di unità hanno lo scopo di testare l'interfaccia nelle classi, non nell'implementazione interna. In questo modo, le modifiche agli interni interromperanno i test solo se l'interfaccia è stata compromessa.

Dai un'occhiata al mio answer per una domanda simile e la discussione che segue. È un argomento controverso, certo, ma questo è il mio $ 0,02.

-2

Yeap, opzione GCC piuttosto utile, ma MSVC non ha nulla di simile.
Usiamo macroses invece:

#define class struct 
#define private public 
#define protected public 

^_^

+0

'#define struct' esplode in' template ' – Xeverous

2

Normalmente cerco di utilizzare solo l'interfaccia pubblica delle mie classi nei test unitari. Test Driven Development/Design aiuta molto qui in quanto le classi risultanti tendono ad abilitare questo tipo di test unitario.

Tuttavia a volte è necessario consentire a un'unità di test di accedere a membri non pubblici, ad esempio sostituire il contenuto di un Singleton con un'istanza Fake. Per questo uso la protezione dei pacchetti in Java e gli amici in C++.

Alcune persone sembrano piegarsi all'indietro per evitare gli amici ma dovrebbero essere utilizzate quando appropriato e il loro uso non compromette il design. Sono anche dichiarativi e consentono agli altri programmatori di sapere cosa stai facendo.

1

Wow, questo ha funzionato perfettamente per me.

Ero preoccupato che non fosse necessario che i miei insiemi di accesso ai membri privati ​​di classi costruite in altre librerie dinamiche (file .so), ma è proprio quello di cui ho bisogno.

Ho solo bisogno di dichiarare la bandiera sul mio test di unità .so compila (ogni test è un .so). Neanche sulle librerie in cui sono definiti gli oggetti a cui si accede.

Ne avevo bisogno per accedere a widget interni su un modulo per riempire i loro valori; non sono visibili al resto del programma, ma sono necessari se i miei test devono rappresentare un input da parte dell'utente.Ho pensato di condividere un caso d'uso per quegli scettici accesso privato :)

anche per completezza, ecco la mia classe di form, che mostra il campo name_ privato:

struct EditProduct : public widgets::BusinessObjForm<model::Product> { 
public: 
    EditProduct (WContainerWidget *parent=0); 
protected: 
    void fillObjFields(); 
private: 
    // Consts 
    static const double minPrice = 0.0; 
    static const double maxPrice = 10000.0; 
    // Fields 
    WLineEdit* name_; 
    WTextEdit* description_; 
    WSpinBox* price_; 
    WFileUpload* image_; 
    // Methods 
    bool validate(); 
    void saveProduct(const WString& message); 
}; 

Ed ecco l'inizio della mia unità prova l'accesso a quel widget:

BOOST_AUTO_TEST_CASE(form_save_test) 
{ 
    EditProduct form(app.root()); 
    string txt = "this is a product"; 
    form.name_->setText(txt); 
    BOOST_CHECK_EQUAL(form.name_->text(), txt); 
}