Memory leaks are a serious problem to those who manage their own pointers. So it would be nice to be able to check unit tests for memory leaks. Here’s how to do it with CppUnit. This will work only in Visual Studio and only in Debug, because it uses the CRT debugging facilities.
Unit-testing memory leaks
The basic trick: Use _CrtMemCheckpoint() to obtain the number of allocated blocks (or bytes). In setUp(), save this number in a global / static variable. In tearDown(), check if this number has changed.
However, not every block that is allocated and not freed is a memory leak. Sometimes it’s part of the initialization of a global variable. Therefore, you’ll have to run the tests twice, and only the second time check for memory leaks. By the second time, all globals will have been initialized.
So you need a global bool, let’s call it CheckForMemoryLeaks, and you have to program your test runner to set it to false, run all required tests, set it to true, and run the same tests once more. Now in tearDown(), if the amount of allocated memory has changed, throw an exception, but only if CheckForMemoryLeaks is true.
Searching for the leaks
To find the leak, use _CrtMemDumpAllObjectsSince(). Pass it the _CrtMemState obtained in setUp(). In the Output pane of Visual Studio, in Debug, you will see a list of all blocks allocated during the test, in reverse order. For each block you will see an allocation ID.
Now run the test again in Debug, using _crtBreakAlloc to stop on one of the leaked allocations. It’s best to start from the first allocation, then if it doesn’t give you a clue, you can break on the next one.
Questions and answers
Q: Aren’t setUp() and tearDown() called only once per fixture?
A: No. Otherwise a failing test could mess up the setup and make other tests fail as well.
Q: Are exceptions caught in tearDown()?
A: Yes, but the error message is “Error in tearDown()” instead of the actual exception. I’ll describe a way to display more descriptive error messages some day.
The best way to fight memory leaks is to avoid them in the first place. That’s a very simple matter, just use std::vector and boost::shared_ptr, and everything will be OK. Most of the time.