2012-02-08 28 views
5

¿Cuáles son las formas de probar la unidad de un patrón Singleton en C++? (con ejemplos, por favor)¿Cómo probar la unidad Singleton class - C++?

+1

Esto se parece a dos preguntas bastante separados. Tal vez usted podría mover la parte de inyección de dependencia en una nueva pregunta? –

+0

@GeorgFritzsche Se dice que la inyección de dependencia es una forma de probar la unidad, ¿no? –

+0

"Inyectar un singleton en ..." y "probar una unidad de singleton" son dos temas diferentes. –

Respuesta

8

Haga que la implementación del singleton sea una clase separada, y haga un contenedor que implemente la "singletonness" fuera. De esa manera se puede probar la aplicación tanto como te gusta (excepto el comportamiento Singleton, que es trivial e innecesario.

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 es solo una clase de la cual solo puede tener una instancia. Cómo se encuentra esa instancia no es parte de ese patrón, por lo que el uso de DI con un singleton está completamente bien.

La prueba de la unidad del patrón de singleton es difícil, ese es uno de los principales argumentos en contra de su uso. Una forma de hacerlo en C++ es tener la definición de singleton en una unidad de compilación separada, para que pueda vincularse con una implementación simulada al probar las clases que usan el singleton.

+1

¿Puedes mostrar un pequeño ejemplo aquí? –

1

Ésta es la forma en que lo haría

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

Luego de probar me gustaría crear sólo para propósitos de prueba

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

A continuación, utilice TestSingleton - por lo que son capaces de realizar todos los casos de prueba y asegúrese de que sea el comienzo en que se recrea la instancia.

+0

¿No creaste el constructor privado? –

+0

Ese es el bit '...' - Supuse que podrías completar esos bits. –

+0

Pensé que fue deliberado por su parte. –

2

Supongamos que tenemos el anti-patrón singleton clásico, que es responsable de tres cosas:

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

    // Public interface 
    void do_something(); 

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

y una clase que depende de esto:

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

    void do_something_else(); 

private: 
    Singleton & s; 
}; 

Ahora nos gustaría escribir una prueba unitaria para el singleton:

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

y para la clase dependiente:

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); 
} 

Vemos que hay tres problemas que superar:

  1. No podemos crear y destruir los casos durante las pruebas;
  2. No podemos definir nuestras propias subclases para probar cómo se utiliza la interfaz;
  3. No podemos proporcionar nuestra propia dependencia a la clase dependiente.

La forma más fácil de superar estos problemas es refactorizar la clase Singleton:

  1. hacer público el constructor y el destructor, la responsabilidad de gestión de vida de la clase en movimiento;
  2. Haz que la interfaz sea abstracta, lo que nos permite definir nuestras propias implementaciones;
  3. Elimina la instancia global y modifica las clases dependientes para tomar su dependencia por referencia.

Así que ahora nuestras clases se parecen:

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; 
}; 

Ahora la clase es fácil de probar, y casi tan fácil de usar en la producción (sólo tiene que crear una instancia de RealSingleton y pasar referencias donde se necesitan).El único problema es que ya no puedes llamarlo singleton.

+0

Te daré seguimiento pronto, aquí y en tu otra respuesta. Gracias. –

Cuestiones relacionadas