UnitTests und C++



  • Hallo,

    Da ich gerade eine Reihe UnitTests (mit CPPUNIT) zu einer bereits bestehenden Applikation schreiben darf, stehe ich immer wieder vor einem Problem:

    Eine Klasse benützt eine andere Klasse dessen Verhalten ich entweder nicht testen will oder die in meiner Testumgebung gar nicht richtig funktioneren kann. Nun wäre es meist kein Problem eine Mock-Klasse dafür zu schreiben, nur, wie ersetzte ich das Objekt mit dem Mock? Mittels einer Factory die Interfaces ausspuckt? Diese Lösung finde ich allerdings schlecht, sie ist mit sehr viel Aufwand verbunden, generiert unnötig neuen Code und ich erhalte einen Runtime-Polymorphismus den ich gar nie brauche und auch nie will. Gut, die Factory könnte ich mir sparen und meine Testklasse als friend deklarieren, um die Interfaces komme ich aber nicht rum.

    Das Problem muss doch immer wieder auftauchen, was gibt es sonst noch für Lösungen?

    Ich bin für jede Antwort dankbar!



  • keiner eine Idee?



  • hast du beispielcode?



  • Hi,

    PseudoCode:

    class Manager
    {
    friend class ManagerTest;
    public:
    Manager() : m_worker( new Worker() )
    {
    }
    
    void doSomething()
    {
      m_worker->doSomething();
    }
    
    private:
    Worker* m_worker;
    };
    
    class Worker;
    class WorkerMock;
    
    class ManagerTest
    {
    ....
    void testManager()
    {
      Manager* manager = new Manager(); 
      manager->m_worker = new WorkerMock() // geht so natürlich eben nicht
      manager->doSomething();
      CPPUNIT_ASSERT( manager->m_worker->didSomething() );
    } 
    ...
    };
    

    Ich hoffe das Beispiel ist verständlich...



  • ok, letzter push...



  • Naja. Ohne Laufzeitpolymorphismus wird das wohl lediglich durch Kompilierzeitpolymorphismus gehen, oder sogar noch früher. Ich nenn das mal Präprozessorpolymorphismus.. 🙂

    Sowas ähnliches wirst du wahrscheinlich auf jeden Fall brauchen.

    #ifdef TEST
    #define WorkerType WorkerMock
    #elif
    #define WorkerType Worker
    #endif
    


  • oder "Linktime-Polymorphismus": Da der Mock und der Original Worker das gleiche öffentliche Interface anbieten sollten, reicht es wenn die .cpp andere Implementationen für die Methoden schreibt:

    //worker.hpp 
    class Worker 
    {
    public:
      void foo();  
    };
    
    //worker.cpp
    #include worker.hpp
    void Worker::foo()
    {
      //mach ernst
    }
    
    //Mock_worker.cpp
    #include worker.hpp
    void Worker::foo()
    {
      std::cout << "call of Worker::foo()" << std::endl;  
    }
    

    beim Linken dann je nach Bedarf die Mock_worker.o oder die worker.o mit den anderen Objektdateien zusammenlinken - fertig...
    So gibts keine unnötigen Workarounds im Quelltext für den Einbau der Testklassen


Log in to reply