WinUnit and Structured Exception Handling (SEH)

Recently I was working on a native C/C++ library, and I was searching for a good unit testing framework to use with it. I took a look at TUT, CppUnit, and a couple of others, and was trying to decide which one to choose, when a new issue (February 2008) of the MSDN Magazine arrived. Turned out it came just in time because of the little gem it contained: an article by Maria Blees describing WinUnit, a unit testing framework she developed.

I downloaded the code, compiled the WinUnit.exe program from it, gave it a try, and immediately liked what I saw. Most of all I was impressed by how easy it was to get started: all I needed to do was create an empty DLL project, specify WinUnit.exe in its command line for the debugging, and presto, I was ready to add unit tests to my library!

Of course, designing the unit tests themselves was not that easy, but WinUnit made the process painless and even fun (if unit tests can be fun at all :-) ) Let me give a small example: suppose that for some reason you want to have your own version of the strlen() function, let’s call it MyStrLen(). It should accept a pointer to a TCHAR string and return the number of characters it contains:

size_t 
MyStrLen( LPCTSTR psz ) 
{ 
    size_t nLen = 0;           

    if ( psz == NULL ) 
        throw _T("Null pointer!"); 

    while( *psz++ ) 
         nLen++;            

    return nLen; 
}

How would you test this function with WinUnit? Here is how I would do it:

#include <WinUnit.h> 
#include "MyStrLen.h"           

BEGIN_TEST( MyStrLen_ShouldReturnZeroForEmptyString ) 
{ 
    WIN_ASSERT_ZERO( MyStrLen( _T("") ) ); 
} 
END_TEST           

BEGIN_TEST( MyStrLen_ShouldReturnCorrectLengthForNotEmptyStrings ) 
{ 
    WIN_ASSERT_EQUAL( 1, MyStrLen( _T("a") ) ); 
    WIN_ASSERT_EQUAL( 2, MyStrLen( _T("ab") ) ); 
    WIN_ASSERT_EQUAL( 26, MyStrLen( _T("abcdefghijklmnopqrstuvwxyz") ) ); 
} 
END_TEST           

BEGIN_TEST( MyStrLen_ShouldThrowForNullPointer ) 
{ 
    WIN_ASSERT_THROWS( MyStrLen( NULL ), LPCTSTR ); 
} 
END_TEST

Pretty self-explanatory, isn’t it? Each test gets a name, starts with the macro BEGIN_TEST, and ends with the macro END_TEST. The tests call the function being tested and verify the results using various WIN_ASSERT… macros provided by WinUnit. For the more complex tests one can also use the “fixtures”, and the macros that go with them: FIXTURE, SETUP, TEARDOWN, BEGIN_TESTF, and END_TESTF. See the article for the details.

To run the tests, you just press the F5 key in Visual Studio IDE, and WinUnit.exe loads your test DLL, executes all the tests it finds in it, and sends the output directly to the output window of Visual Studio. If a test does not check, you can double click on the “failed” message in the output window and it takes you right to the line that failed the verification, letting you see what’s going on. In short, it works just the way I would expect it to work.

One problem I encountered was when trying to write tests for functions that were using the Structured Exception Handling (SEH) mechanism instead of the C++ exceptions. (Why did I need that? Because the library I’ve been writing contains quite a few of pure C code (not C++), and you cannot use C++ exceptions in the C files). When trying to compile tests for such C files, the compiler complained that it couldn’t mix the SEH and C++ exceptions in the same function. Having looked into the WinUnit.h file, I’ve come up with the following macros that can be used instead of the usual ones:

BEGIN_TEST_SEH( TestName ) 
END_TEST_SEH 
FIXTURE_SEH( FixtureName ) 
SETUP_SEH( FixtureName ) 
TEARDOWN_SEH( FixtureName ) 
BEGIN_TESTF_SEH( TestName, FixtureName ) 
END_TESTF_SEH( FixtureName ) 
WIN_ASSERT_THROWS_SEH( expression, ... )

(I’ve put them in a separate file, WinUnitSEH.h).

Now, if the MyStrLen() function were throwing a SEH exception, for example by calling the RaiseException() function instead of using the throw operator, the last test could be rewritten as follows:

#include <WinUnitSEH.h>          

BEGIN_TEST_SEH( MyStrLen_ShouldThrowForNullPointerSEH ) 
{ 
    WIN_ASSERT_THROWS_SEH( MyStrLen( NULL ) ); 
} 
END_TEST_SEH

Hope this helps someone. Download WinUnitSEH.h