2012-02-08 23 views
5

Quali sono i modi per testare un modello Singleton in C++? (con esempi per favore)Come testare la classe Singleton - C++?

+1

Questo appare come due domande piuttosto separati. Forse si potrebbe spostare la parte iniezione di dipendenza in una nuova domanda? –

+0

@GeorgFritzsche L'iniezione di dipendenza si dice che sia un modo per testare le unità, non è vero? –

+0

"Iniettare un singleton in ..." e "unit test a singleton" sono due soggetti diversi –

risposta

8

Rendere l'implementazione del singleton una classe separata e creare un wrapper che implementa la "singletonness" all'esterno. In questo modo è possibile verificare l'attuazione quanto più vi piace (tranne il comportamento Singleton, che è banale e inutile.

class SingletonImpl { 
public: 
    int doit(double,double); 
}; 

class Singleton { 
public: 
    Singleton& instance() {...} 
    int doit(double a,double b) {impl->doit(a,b);} 
    ... 
private: 
    SingletonImpl impl; 
} 
0

Un singleton è solo una classe di cui è possibile avere solo un'istanza. Come trovi che l'istanza non fa parte di quel modello, quindi usare DI con un singleton è completamente soddisfacente.

Test delle unità Il modello di singleton è difficile, che è uno degli argomenti principali contro l'utilizzo. Un modo per farlo in C++ è quello di avere la definizione del singleton in un'unità di compilazione separata, in modo che sia possibile collegarsi a un'implementazione di simulazione durante il test delle classi che utilizzano il singleton.

+1

Puoi mostrare un piccolo esempio qui? –

1

Questo è come io lo farei

class Singleton 
{ 
    protected: 
     static Singleton *instance = 0: 
    public: 
     Singleton &GetInstance() 
     { 
      if (!instance) instance = new Singleton; 
      return *instance; 
     } 
     ... 
}; 

Poi per testare Vorrei creare solo a scopo di test

class TestSingleton : public Singleton 
{ 
    public: 
     void DestroyInstance() { 
      if (instance) delete instance; 
      instance = 0; 
}; 

Quindi utilizzare TestSingleton - così si è in grado di eseguire tutti i casi di test e assicurarsi che sia l'inizio ricreata dell'istanza.

+0

Non hai creato il costruttore privato? –

+0

Questo è il bit "..." - ho pensato che potessi riempire quei bit. –

+0

Ho pensato che fosse stato intenzionale da parte tua. –

2

supponiamo di avere la classica Singleton anti-modello, che è responsabile per tre cose:

class Singleton { 
public: 
    // Globally accessible instance 
    static Singleton & instance(); 

    // Public interface 
    void do_something(); 

private: 
    // Lifetime management 
    Singleton(); 
    ~Singleton(); 
} 

e una classe che dipende da questo:

class Dependent { 
public: 
    Dependent() : s(Singleton::instance()) {} 

    void do_something_else(); 

private: 
    Singleton & s; 
}; 

Ora vorremmo scrivere un unit test per il singleton:

void test_singleton() { 
    Singleton s;  // Problem 1 
    s.do_something(); 
    assert(/* some post-condition */); 
} 

e per la classe dipendente:

struct StubSingleton : Singleton // Problem 2 
{ 
    int did_something; 

    StubSingleton : did_something(0) {} 
    void do_something() {++did_something;}  
}; 

void test_dependent() { 
    StubSingleton s; // Problem 1 
    Dependent d(s);  // Problem 3 
    d.do_something_else(); 
    assert(s.did_something == 1); 
} 

Vediamo ci sono tre problemi da superare:

  1. Non possiamo creare e distruggere le istanze durante le prove;
  2. Non possiamo definire le nostre sottoclassi per verificare come viene utilizzata l'interfaccia;
  3. Non possiamo fornire la nostra dipendenza alla classe dipendente.

Il modo più semplice per superare questi problemi è quello di refactoring la classe Singleton:

  1. rendere pubblico il costruttore e distruttore, la responsabilità della gestione vita fuori della classe in movimento;
  2. Rende l'interfaccia astratta, permettendoci di definire le nostre implementazioni;
  3. Rimuovere l'istanza globale e modificare le classi dipendenti per prendere la sua dipendenza per riferimento.

Così ora le nostre classi assomigliano:

class Singleton { 
public: 
    virtual ~Singleton() {} 
    virtual void do_something() = 0; 
}; 

class RealSingleton : public Singleton 
{ 
    void do_something(); 
}; 

class Dependent { 
public: 
    explicit Dependent(Singleton & s) : s(s) {} 
    void do_something_else(); 
private: 
    Singleton & s; 
}; 

Ora la classe è facile da testare, e quasi altrettanto facile da usare nella produzione (si solo bisogno di creare un'istanza di RealSingleton e superare i riferimenti a dove sono necessari).L'unico problema è che non puoi più chiamarlo singleton.

+0

Seguirò presto, qui e sulla tua altra risposta. Grazie. –