lundi 25 février 2019

How horrible is Static-Casting Base class to Derived class in order to call destructor in tests (for tests)?

Preface: I am aware that the following is a very dirty hack. We will refactor things (so we can write non-hacky tests, use Mocks and possibly even get rid of the Singletons), but first we need test coverage...

As said before, we have a significant number of "Singleton" type classes in our project, i.e. something along the lines of.

class SomeSingleton
{
  public:
    SomeSingleton& getSingleton()
    { 
        if (m_singletonInstance == nullptr)
            m_singletonInstance = new SomeSingleton();
        return *m_singletonInstance;
    }

  protected:
    SomeSingleton();
    virtual ~SomeSingleton();

    static SomeSingleton* m_singletonInstance = nullptr;
}

In order to properly run our unit tests the Singleton needs to be "reset" (or data from our previous tests may persist in the singleton, influencing the current test). Unfortunately there is no feasible way to do that (the Singleton instance and the destructor are protected).

We do not wish to change our production code to contain test-only functionality (i.e. no forward declaring a ::test::SomeSingletonResetter as a friend in SomeSingleton, no introducting a public void resetSingleton_forTestUseOnly() function etc.) - and we also wish to avoid any significant changes to the production code until we have test coverage in place.

So, here's the dirty hack we are considering: In the test project we derive a very simple wrapper class (i.e. no members) from the Singleton with a static function that deletes the singleton - however as the destructor cannot be called even from the derived class (it is protected) we would static_cast the singleton to the derived class and delete that:

class SomeSingletonWrapper : public SomeSingleton
{
  public:
    SomeSingletonWrapper();
    ~SomeSingletonWrapper() override;

    static void reset()
    {
        //can't delete/call destructor on SomeSingleton since it is protected
        delete static_cast<SomeSingletonWrapper*>(m_singletonInstance);
        m_singletonInstance = nullptr;
    }
}

Our thoughts are, the classes are essentially the same size, and the derive classes' destructor will call the base destructor, so the class gets destroyed properly. Can this work (until we can refactor things) or will it horribly explode in our faces? And is there a better (less hacky) way to archive this (without majorly modifying the production code)?

Aucun commentaire:

Enregistrer un commentaire