Testarea unui singleton folosind Mock Object

Problema

De multe ori testarea singleton-urilor este una foarte anevoioasa. Folosirea acestui pattern pe langa ca aduce multa simplitate in ceea ce priveste accesul la variabilele protejate de singleton ingreuneaza testarea codului.

Urmatorul cod prezinta solutia clasica, si nu neaparat cea mai buna, de implementare a unui singleton:


class Singleton
{
  public:
    static Singleton* getInstance()
    {
       if (!instance)
       {
          instance = new Singleton;
       }
       return instance;
    }

  private:
    Singleton(){}
    static Singleton* instance;
};

Pentru a utiliza acest singleton in cadrul altor clase avem de facut fie Singleton::getInstance() sau folosind tehnica de Dependency Injection.

Folosind prima metoda vom avea un cod foarte greu de testat datorita faptului ca este greu sa cream un mock astfel incat in momentul in care se va face getInstance() sa fie returnata instanta mock-uita si nu cea originala.

Exemplificare solutie

O solutie pentru a rezolva aceasta problema este combinarea celor doua patternuri creationale prezentate si anume Factory Method Design Pattern respectiv Singleton Pattern. Am gasit aceasta metoda cautand solutii optime pentru testarea claselor folosind GoogleMock framework:


#include <iostream>
#include <functional>

class A
{
   virtual ~A(){}
   virtual void foo() = 0;
};

class Areal : A
{
   virtual void foo(){
      std::cout<<"real"<<std::endl;
   }
};

class Amock : A
{
   virtual void foo(){
      std::cout<<"mock"<<std::endl;
   }
};

class Single
{
   typedef std::function< A*() > CreatorFn;

   static A* getInstance(){
       if (!instance)
          instance = (create())();
       return instance;
   }

   static void setCreator( CreatorFn newFn ){
       create() = newFn;
   }

private:

   static CreatorFn& create(){
       static CreatorFn f( [](){return new Areal;} );
       return f;
   }
   static A* instance;

};


bool useMock = true;

int main()
{
   if ( useMock ){
       Single::CreatorFn mockFn( [](){ return new Amock; } );
       Single::setCreator( mockFn );
   }

   Single::getInstance()->foo();
}

Concret vom aveam o interfata si doua implementari diferite ale acesteia.

class A
{
   virtual ~A(){}
   virtual void foo() = 0;
};

class Areal : A
{
   virtual void foo(){
      std::cout<<"real"<<std::endl;
   }
};

class Amock : A
{
   virtual void foo(){
      std::cout<<"mock"<<std::endl;
   }
};

Definirea singleton-ului desi in cazul prezentat mai sus este facuta intr-o alta clasa (Single) poate fi facuta chiar clasa de baza si anume in cazul nostru A.

Solutia pe scurt

  • Creati clasa folosind metoda Singleton
  • Creati folosind patternul Factory Method o metoda prin intermediul careia sa puteti injecta clasei o metoda care sa contina crearea + initializarea obiectului ce va defini singletonul.
  • Singletonul va avea “by default” o metoda standard de creare a obiectului aceasta metoda putand fi suprascrisa in cazul testelor. Se observa in cadrul solutiei folosirea unui lambda function ce defineste partea de creare a “mock-ului”.
    
    if ( useMock ){
         Single::CreatorFn mockFn( [](){ return new Amock; } );
         Single::setCreator( mockFn );
    }
    Single::getInstance()->foo();

no responses for Testarea unui singleton folosind Mock Object

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.