2010-04-09 18 views
5

Supponiamo che io sono una classe C++ in questo modo:C++ e Dependency Injection in unit testing

class A 
{ 
    public: 
     A() 
     { 

     } 

     void SetNewB(const B& _b) { m_B = _b; } 

    private: 
     B m_B; 
} 

Al fine di test di unità qualcosa come questo, avrei dovuto rompere la dipendenza di A sul B. Poiché classe A tiene su un oggetto reale e non un puntatore, dovrei refactoring questo codice per prendere un puntatore. Inoltre, avrei bisogno di creare una classe di interfaccia genitore per B in modo da poter passare il mio falso di B quando eseguo il test di SetNewB.

In questo caso, il test dell'unità con l'iniezione di dipendenza non complica ulteriormente il codice esistente? Se creo un puntatore B, ora sto introducendo l'allocazione dell'heap e ora una parte di codice è responsabile della sua pulizia (a meno che non utilizzi i puntatori conteggio ref). Inoltre, se B è una classe piuttosto banale con solo un paio di variabili e funzioni membro, perché introdurre un'intera nuova interfaccia per questo invece di provare solo con un'istanza di B?

Suppongo che si possa argomentare che sarebbe più semplice rifattorizzare A usando un'interfaccia. Ma ci sono dei casi in cui due classi potrebbero dover essere strettamente accoppiate?

risposta

8

Penso che tu stia prendendo in considerazione l'idea di Unit Testing. In questo caso, A e B sono un'unità, cioè A non può esistere senza B. Innanzitutto, prova B e assicurati che superi tutti i test unitari specifici per B, quindi una volta che passa, prova A e assicurati che comportati come dovrebbe.

+0

Interessante, suppongo che stavo immaginando che i test unitari richiedessero che tutte le classi fossero completamente separate l'una dall'altra. Ma come fai notare, A ha assolutamente bisogno che B lavori; quindi sono in realtà una sola unità. – lhumongous

0

A seconda di come si utilizza B, è possibile utilizzare uno std :: auto_ptr invece di un puntatore conteggio rif. Fintanto che non è necessario passare un riferimento a B a qualcos'altro che potrebbe funzionare e richiedere modifiche minime al codice.

2

Se B è in realtà una dipendenza immessa in A, è necessario considerare una serie di altre opzioni. In primo luogo sta iniettando B in tempi di costruzione:

class A 
{ 
    public: 
     A(const B& _b) : m_B(_b) {} 

    private: 
     const B& m_B; 
}; 

Se davvero si vuole modificare B durante il ciclo di vita del vostro A oggetto, poi chiedetevi se siete davvero il proprietario del B. In caso contrario, passare un puntatore ad esso e assumere che colui che lo passa è responsabile per il suo ciclo di vita - questo è un percorso difficile, e potrebbe richiedere l'utilizzo di puntatori conteggiati con riferimento.

Se quello che vuoi fare è prendere una copia di B per te allora potresti definire un metodo clone() su B. Se si desidera creare il proprio oggetto utilizzando le informazioni contenute in B, è possibile iniettare un MyBFactory nel costruttore e utilizzarlo con questo B.