High Integrity Unit Testing with advanced DUnit V9.3 code.


DUnit is an excellent aid to creating and maintaining code to meet design criteria. It provides a very convenient framework to test pieces of code as they are written and as they are modified later. Automated testing after each code change gives confidence that code will perform as designed when deployed in production code. During development a change in one piece of code may pass it's associated unit tests but still cause failures elsewhere in the project. Project wide automated test suites help catch such errors early, while the developer is focused on the recently altered code and so significantly reduces debugging time and effort. Although DUnit has been closely allied with "Xtreme" techniques it is equally valid as a tool for individuals or disparate groups and quality assurance testers.

Empty Test Detection
Memory Leak Detection
Handling legitimate leaks in test suites
Memory Leak Detection at Shutdown
Showing TestCase Run-Time Applied Properties
Displaying Overridden GUI Settings
Finding TestCases containing armed Run-Time Properties
Displaying Allowed Leak Size in Run-Time Properties
Halt Repeated tests on first failure
Design Goals
Summary of New Features
Terms used

Empty Tests.

Previous versions of DUnit can convey a false sense of completeness when unit tests are left empty or fail to call Check() within an individual test case's test procedure.
Completely empty tests only failed when compiled with "Optimization ON" and passed otherwise. Developers frequently compile with "Optimization OFF" and so can easily overlook empty tests while a test suite is being developed. All tests containing at least one valid Delphi statement will always pass, regardless of the compiler optimization state. Instances have been encountered where complex test cases contain calls to Check() but the execution path bypasses the Check() statement. Such tests always pass, regardless of errors in the code under test, giving an optimistic impression of code correctness.
To detect all empty tests, regardless of compiler optimization and test cases which fail to call Check() select the GUI menu option as shown below.

Images shows Fail when Check not called Menu

In the rare circumstance where it is inappropriate to call Check() from within a test case, the global GUI setting can be overridden to prevant false test failures. This is achieved by placing the following statement within the individual test case's test procedure.

FailsOnNoChecksExecuted := False;

Empty test failures take precedence over memory leak failures.

Back to Top

Memory Leak Detection.

The effect of memory leaks in production code varies from no ill effects to shutdown disasters.
Production code can be executed by memory and resource leak detection tools such as CodeWatch and MemProof but it's a tedious and non automated task. The code sequence leading to leaks and hence their discovery may never be exercised in the tester's environment. Whilst individual test cases can be also executed under the same tools, the tedium of repeatedly running more than a few tests becomes unrealistic.

Test scenarios may create objects, strings, exceptions and populated dynamic arrays, perform some function and finally call Check() to report success or failure. With DUnit's new leak detection enabled if the test case passes but memory is leaked the test case now fails, The error message shows the imbalance for, Setup, test procedure or TearDown. DUnit reports memory "imbalance" i.e. where memory is consumed or retuned to the memory manager so the developer can make informed decisions about leak locations. Test cases do not fail where all memory used during the test is returned to the memory manager by the end of TearDown. Failures can optionally be ignored for SetUp and TearDown, however many unit tests are structured such that TearDown does the cleanup so this option may change in later versions.

Leak detection is globally enabled from the GUI under the "Options" main menu.

Image shows Detect Mem Leaks Menu

Below we see the result of running Leak Detection on code which was previously thought to be free of memory leaks.
Deeper investigation can reveal however that leaks may be legitimate in the context of unit testing and not problems in the application code under test.

Image shows memory leaks detected during test suite execution

Executing test suites a second time without closing DUnit generally recovers assigned strings memory.
This is particularly noticeable where strings declared and assigned in test cases are not cleared in TearDown.
The string' ref count may not decrement to zero until DUnit closes.
The screen shot below shows the dramatic effect or re-executing the test suite a second time without closing the test project.

Image shows the effect of reducing string leak reports after executing the test suite a second time

Back to Top

Handling legitimate leaks in test suites.

Unit test Setup and Teardown itself can legitimately contain code which does not release memory back to the memory manager. To prevent failure of a test case containing legitimate memory retention the developer can choose between several options. The first offers the least protection against real code leaks and simply prevents failure on any leak size.
Adding the following code to the test procedure or alternatively to SetUp if all tests in the suite need to be prevented from failing.
FailsOnMemoryLeak := False;

If the legitimate leak is of known size the developer can add the following code to the pest procedure.
AllowedMemoryLeakSize := xxx; // where xxx is in bytes, as reported but the executed test.

In a multi threaded environment FastMM does not guarantee to always allocate a predictable size of memory and may allocate a larger block than requested. Other situations arise too where string lenghts may be differ from day to day, for instance when a long date format is used. Consequently it may be advisable to allocate several discrete memory leak sizes.
The developer can the add the following code to the test procedure.
SetAllowedLeakArray([XX, YY, ZZ]); //to a maximum of three values, where XX,YYand ZZ are in bytes, as reported by the executed test.

The user can also globally ignore leaks caused solely by code in Setup and Teardown and just remain armed to fail on leaks in the test procedure.

Image shows Ignore Setup TearDown Leaks Menu

The above GUI setting can be overridden at the test case level by setting:-
IgnoreSetUpTearDownLeaks := True; //False as appropriate.

Lastly, memory recovered by a test that was leaked by the prior test will show as a negative leak. Currently there is a run-time property to force a failure if this occurs. This feature will probably disappear in future versions. Ideally tests should not be written to rely on any other tests having been executed. Having said that, in a QA test environment this is precisely what happens. Real leak sizes might therefore be misleadingly reported on some tests when run in succession and be accurate when run in isolation. Keep that in mind while debugging code.

Back to Top

Leak Detection at Shutdown.

Finally, FastMM provides a leak report on shutdown which has a more detailed explanation of what leaked but applies to the whole test executable. It is of most value when it can be targeted at specific test cases that are run in isolation then DUnit is shut down.
Leak detection on shutdown is invoked at the GUI level regardless of other leak detection settings.

Images shows Report Leaks on Shutdown Menu

FastMM's report looks like this:-

FastMM leak report on shutdown

Leak reporting on shutdown is automatically available when the code is compiled under BDS2006 and the Unit Tests are executed from the IDE.
For Delphi5..Delphi2005 the following applies.
As shipped, FastMM4options.inc does not define "ManualLeakReportingControl". To invoke a leak report on shutdown ManualLeakReportingControl needs to be defined either of 2 ways.
1 In Delphi, Project - Options - Conditional defines, before the project is compiled, or
2 In FastMM4options.inc by removing the . in the line containing {.$define ManualLeakReportingControl}

Leak reporting as described is only available when the code is executed from the IDE.
To obtain leak reporting on shutdown outside Delphi's IDE follow the instructions in FastMM4options.inc.

Back to Top

TestCase Run-Time Applied Properties Popup Menu.

If you have read this far you may have noticed, that code affecting the new modes of test failure can be placed within Setup, (affecting all tests within a test class) or within individual test procedures. This code obviously has no effect until the test actually runs. Nor does the code have any effect if an ordinary failure occurs because the latter takes priority in being reported.

This web page title mentions "High Integrity Unit Testing". So what are the implications of overriding at the test procedure level a GUI command to fail a test. Such overrides could go undetected, buried deep in test code and silently mask potential failures. Consequently, a Popup Menu has been added, which reports the actual properties that applied when the test completed execution.

To Access the Run-Time Applied Properties:-
1 Select an individual test case in the Test Hierarchy.
2 Right click and select TestCase Properties at the top of the popup menu. -or-
2 Press Crtl+Shift+T.

The basic popup menu is as shown below.

Run Time Properties Menu

Back to Top

Displaying Overridden GUI Settings.

The purpose of overriding a global GUI setting such as FailsOnNoChecksExecuted is to prevent an inappropriate failure.
A good example of this situation occurs in DUnit's own UnitTests project and so is illustrated below. Not all tests need to call Check() because the test might not have a checked result. Consequently when a test legitimately does not call Check() we place a GUI override statement in the test procedure.
FailsOnNoChecksExecuted := False;

Subsequently, when the test is executed the value of a new Results View value "Overrides" is incremented. This serves to indicate, regardless of the GUI option "Warn if Fail Test Overridden" setting.
To assist developers in locating Overridden GUI settings an Options menu item is provide, "Warn if Fail Test Overridden". When checked and underlaying test procedure passes the result is shown in Yellow instead of Green.

DUnit test hierarchy shows overridden GUI settings highlighted in yellow

Selecting the Run-Time Applied Properties shows that the GUI command to "Fail TestCase if no checks executed" has been overridden at the test case level and is highlighted in Yellow.

Run-Time Properties popu menu shows overridden GUI settings highlighted in yellow

It is important to be aware, that in this scenario, Yellow signifies that a potential failure has been masked. The reverse situation where a test case is armed to fail but the GUI is not requesting a failure does not produce a Yellow "Caution". If a failure results, when the test suite is run from the GUI the developer's attention will be grabbed by the familar Magenta color against test case and the Failure count increments.

Finding all TestCases that contain assertive Run-Time Properties

The following non persisted menu option is provided to highlight test cases, that are armed at the test case level to fail when any of the following are set true. "FailsOnNoChecksExecuted", "FailsOnMemoryLeak", "FailsOnMemoryRecovery" or IgnoreSetUpTearDownLeaks.
The same function also highlights test cases containing a non zero "AllowedMemoryLeakSize", regardless of whether "FailsOnMemoryLeak" or "FailsOnMemoryRecovery" are set true.

DUnit test hierarchy shows TestCases having Run-Time Properties disarmed

Note. After selecting either "Warn if Fail Test Overridden" or "Show TestCases with RunTime Properties" it is necessary to re-run the unit tests to display the results.
Running the DUnit "UnitTests" under Win32 with the above selection produces the following result.

DUnit test hierarchy shows TestCases having Run-Time Properties armed. 

Back to Top

Display of AllowedLeakSize in Run-Time Properties

When "Show TestCases with RunTime Properties" is set, test cases with a non zero AllowedLeakSize value are highlighted in yellow after executing the test suite.
Test cases containing a range of allowed leak sizes established using the SetAllowedLeakArray statement are similarly highlighted.
The allowed leak size programmed by the developer is displayed as shown below.

Run-Time properties popup menu showing allowed memory leak size display

When more than one allowed leak size has been set using the SetAllowedLeakArray statement the "intelligent" display shows the value which allowed the test to pass or the lowest value in the set if the test failed.

Back to Top

Halt Repeated Tests on first failure.

DUnit included several Test Decorator classes, which can provide group SetUp and TearDown procedures and the ability to run counted multiple tests. The latter has been expanded to allow a counted test to halt at the first failure, rather than continuing to execute N tests, with the subsequent N failure reports filling up the Failures View.

Design Goals.

The overriding goal in designing the new high integrity additions has been to ensure that existing test suites do not break. This aim has been achieved. Note that no attempt has been made to invoke the new class of "Soft Failure" when the tests are run via TextTestRunner.
Empty Test failures behave exactly as before until the new GUI option "Fail TestCase in no checks executed" is selected.
For D5..D2005 the option to include FastMM is made by a global conditional define. Leak checking Options are disabled until FASTMM is defined.
Similarly post execption leak reporting is disabled until ManualLeakReportingControl is added to the project conditional defines and then corresponding "Report memory leak type on Shutdown" is selected.
Finally the DUnit test program requires additional code in the uses section of the project.dpr to access FastMM. Until FastMM is linked into your DUnit test suite and compiled the Options menu items relating to memory leak detection will remain disabled.

Back to Top

Summary of Additional Features.

Back to Top

Download Links.

DUnit code as described above is available for download from Sourceforge

Full DUnit code is available at SourceForge.org

FastMM now provides Delphi with an advanced memory manager that coupled with the new DUnit code provides automated memory leak detection for every test case. BDS2006 already incorporates FastMM memory manager so Borland/CodeGear have recognized and endorsed it's validity as a memory manager.
Regardless of whether you make use of the upgraded DUnit code, the addition of FastMM to your DUnit test suites and applications may improve the execution time for reasons best spelled out in the FastMM documentation. For D6 users, you will need to add additional code to your .dpr as shown in the UnitTests.dpr from the HIDUnit.zip file.
FastMM can be downloaded from sourceforge.net/projects/fastmm

Back to Top

Terms Used.

Within the context of this document a test case is the code which DUnit executes that the user has written deriving from TTestCase.
A specific test case may be referred to as TestCase, particularly in menus.
A test case always entails a test procedure and optionally include an associated SetUp and TearDown procedure.
A test suite is the collection of test cases associated with a specific derivation of TTestcase.
Similarly test suites are a collection of individual test suites.

Run may sometimes be used in place of test procedure that executes between SetUp and TearDown.
The notion of Run-Time Properties has been introduced to DUnit to describe property values which may be applied to test cases at execution time.
Run time properties have no effect on the outcome of a test if the test encounters an exception or normal test failure. However they are examined by DUnit after the test has executed to optionally raise a Post Execution Failure.
Run time properties may be turned on by GUI options, in which case they are applied by DUnit immediately before SetUp is called. They may also be applied by the user anywhere within Setup or Run. However users cannot expect run time properties to behave as required if they are altered by user code within TearDown.
Soft Failure is sometimes used to describe the new class of failures selectively enabled by run time properties.

Back to Top

Page last updated 13-Feb-2007

Page Hit Counter

Valid HTML 4.0 Transitional