Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing destructors?

Is there any good way to unit test destructors? Like say I have a class like this (contrived) example:

class X
{
private:
    int *x;

public:
    X()
    {
         x = new int;
    }

    ~X()
    {
         delete x;
    }

    int *getX() {return x;}
    const int *getX() const {return x;}
};

Is there any good way to unit test this to make sure x gets deleted without cluttering up my hpp file with #ifdef TESTs or breaking encapsulation? The main problem that I'm seeing is that it's difficult to tell if x really got deleted, especially because the object is out of scope at the time the destructor is called.

like image 332
Jason Baker Avatar asked Nov 23 '08 00:11

Jason Baker


People also ask

What is destructors and example?

A destructor is called for a class object when that object passes out of scope or is explicitly deleted. A destructor is a member function with the same name as its class prefixed by a ~ (tilde). For example: class X { public: // Constructor for class X X(); // Destructor for class X ~X(); };

What do you mean by destructors?

A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete . A destructor has the same name as the class, preceded by a tilde ( ~ ). For example, the destructor for class String is declared: ~String() .

What are the types of destructors?

Destructors cannot be declared const , volatile , const volatile or static . A destructor can be declared virtual or pure virtual . If no user-defined destructor exists for a class and one is needed, the compiler implicitly declares a destructor.

Are destructors necessary?

We know that if a destructor is not provided, the compiler will generate one. This means that anything beyond simple cleanup, such as primitive types, will require a destructor. In many cases, dynamic allocation or resource acquisition during construction, has a clean up phase.


3 Answers

There may be something to be said for dependency injection. Instead of creating an object (in this case an int, but in a non-contrived case more likely a user-defined type) in its constructor, the object is passed as a parameter to the constructor. If the object is created later, then a factory is passed to the constructor of X.

Then when you're unit testing, you pass in a mock object (or a mock factory which creates mock objects), and the destructor records the fact that it has been called. The test fails if it isn't.

Of course you can't mock (or otherwise replace) a builtin type, so in this particular case it's no good, but if you define the object/factory with an interface then you can.

Checking for memory leaks in unit tests can often be done at a higher level, as others have said. But that only checks that a destructor was called, it doesn't prove that the right destructor was called. So it wouldn't e.g. catch a missing "virtual" declaration on the destructor of the type of the x member (again, not relevant if it's just an int).

like image 148
Steve Jessop Avatar answered Oct 01 '22 21:10

Steve Jessop


I think your problem is that your current example isn't testable. Since you want to know if x was deleted, you really need to be able to replace x with a mock. This is probably a bit OTT for an int but I guess in your real example you have some other class. To make it testable, the X constructor needs to ask for the object implementing the int interface:

template<class T>
class X
{
  T *x;
  public:
  X(T* inx)
    : x(inx)
  {
  }

  // etc
};

Now it becomes simple to mock in the value for x, and the mock can handle checking for correct destruction.

Please pay no attention to the people who say you should break encapsulation or resort to horrible hacks in order to result in testable code. While it is true that tested code is better than untested code, testable code is the best of all and it always results in clearer code with fewer hacks and lower coupling.

like image 23
1800 INFORMATION Avatar answered Oct 01 '22 22:10

1800 INFORMATION


I tend to go with a "By any means necessary" approach to testing. If it needs a test, I am willing to leak abstractions, break encapsulation, and hack... because tested code is better than pretty code. I will often name the methods that break this up something like VaildateForTesting or OverrideForTesting to make it clear that the breach of encapsulation is meant for testing only.

I don't know of any other way to do this in C++ than having the destructor call into a singleton to register that it has been destroyed. I have come up with a method for doing something similar to this in C# using a weak reference (I don't violate encapsulation or abstractions with this approach). I am not creative enough to come up with an analogy to C++, but YOU might be. If it helps, great, if not, sorry.

http://houseofbilz.com/archive/2008/11/11/writing-tests-to-catch-memory-leaks-in-.net.aspx

like image 38
Brian Genisio Avatar answered Oct 01 '22 21:10

Brian Genisio