DUnit (the unit testing framework for Delphi) has a nice feature of detecting memory leaks during tests;
that is, it checks total allocated memory before and after running a test,
and if the second value is greater than the first value, it (optionally) reports a test failure.
To enable this feature in DUnit, use the Fail TestCase if memory leaked option in DUnit's Options menu.
However, you can start getting seemingly invalid test failures -
if, during the test, some memory gets allocated, but is freed not in the test itself,
but later on, such as on application shutdown.
This often happens when using system calls, third party libraries, or GUIs
(though, obviously, it can happen with your own code, too).
After all, a common design pattern is to allocate memory in a "lazy" fashion -
on the first call to a routine or on the first reference to an object/class that needs additional memory.
And memory allocated thus is often freed when the class/unit is unloaded from memory -
on application shutdown (in the unit's finalization section, for instance).
For example, let's say that you are testing DB code (using DBExpress and MySQL).
In the test case, you create a connection, do some operations on the DB, and then free the connection.
Because DBExpress allocates some memory on creation of the first connection
and doesn't free it until the application finishes, you get a memory leak:
procedure TYACDBTests.TestMemory;
var
LConnection: TSQLConnection;
begin
LConnection := CreateSQLConnection;
Check(TRUE);
FreeAndNIL(LConnection);
end;
The code is quite ok (where CreateSQLConnection creates the object, sets all parameters, and connects to the DB),
but the memory "leak" is still there - that is, it will be reported by DUnit...
A possible workaround in such cases is to insert "dummy" code
that will force the first allocation before any tests are run,
so that during the tests, the needed memory is already allocated
(thus accounted for when DUnit checks total allocated memory before the test).
You can place this code in the unit's initialization section, for instance:
var
LConnection: TSQLConnection;
initialization
LConnection := CreateSQLConnection;
FreeAndNIL(LConnection);
end.
Now, the TSQLConnection created in the test case doesn't allocate any more memory,
thus DUnit's leak detector stops failing the test.
An easy way to find out scenarios similar to this one (though not bullet-proof)
is when a second run of the testing suite doesn't show the memory leak anymore
(but, a second run without restarting DUnit after the first run).
Then there's a high probability that the first run allocated some memory
that is not being allocated anymore on the second run.
And that's a good candidate for a solution like the one above,
though it's not always that easy to find out what code should be called before our tests...
Top
|