A mini test framework in a single header file Wednesday 31st October 2007
After trying it on a number of projects, I’m now very enthusiastic about test driven development. At home I’ve rather missed the minimal support code that I had at my old job, so I’ve rewritten a miniature test framework in a single header file.
Only this time, it’s better. Framework implies something rather big. This isn’t. The design goal was to make something really simple and lightweight that just makes the process of writing tests as simple as possible with as few overheads as possible.
In this framework a test function is just a void function returning void. If the function doesn’t throw an exception when it is called then it has passed.
Here are some example tests, which show the three different type of assert macros provided. (Yes all the tests are broken!)
void strlen_test() { HSHG_ASSERT( strlen( "1245" ) == 5 ); } void sum_test() { int total = 0; for( int i = 1; i <= 15; ++i ) { total += 1; total += 7; } for( int i = 2; i <= 7; ++i ) { total += i; } HSHG_ASSERT_DESC( total == 148, "Maximum break is 148" ); } class MyThrowable { public: static void ThrowMe() { throw MyThrowable(); } }; void MyThrowable_test() { HSHG_ASSERT_THROWS( true ? 0 : (MyThrowable::ThrowMe(), 0), MyThrowable ); }
The test namespace is HSHGTest and there is a struct TestFn for a test function which contains a char* for the function name and a pointer to the function itself.
There is then an inline function called RunTests that takes an array of these structs (terminated by one with a null function pointer); it runs each test in the array and reports to a given std::ostream; it then returns EXIT_SUCCESS if it ran some tests and they all passed, and EXIT_FAILURE otherwise. This makes it suitable for returning the result directly from a main function. Here is an example report.
testtest.cc:6: Test strlen_test failed. ( strlen( "1245" ) == 5 ) testtest.cc:24: Test sum_test failed. ( Maximum break is 148 ) testtest.cc:38: Test MyThrowable_test failed. ( Exception MyThrowable expected. )
If this sounds a bit laborious then there are some helper macros to set a suitable array up, and even a macro for a default main function:
HSHG_BEGIN_TESTS HSHG_TEST_ENTRY( strlen_test ) HSHG_TEST_ENTRY( sum_test ) HSHG_TEST_ENTRY( MyThrowable_test ) HSHG_END_TESTS HSHG_TEST_MAIN
These macros translate (roughly) into:
namespace { HSHGTest::TestFn tests[] = { { "strlen_test", strlen_test }, { "sum_test", sum_test }, { "MyThrowable_test", MyThrowable_test }, { NULL, NULL } }; } int main() { return HSHGTest::RunTests( tests, std::cout ); }
The "framework" is available for download here. HSHGTest frawework
Looks like ruleage except for the omission of ASSERT_EQUAL( str1, str2 ) with helpful error messages about what part of the string didn’t match.
ASSERT_EQUAL was t’ruleage, but it’s too big for the ultralite HSHG framework. I seem to remember that it was (rightly) in a .cpp.
All my tests are so short at the moment, that I haven’t had a need for something like it yet.