Most TestFrameworks use TestAssertions. E.g. CPPUNIT_ASSERT( m_1_2 == m_1_2 ); TEST_ASSERT( Complex(1,2) == Complex(1,2) ); test_assert( m_1_2 == m_1_2 ); or even the standard assert( m_1_2 == m_1_2 ); It is probably best NOT to use the standard assert(). * by default it aborts the entire program. This means that subsequent tests do not run. Also, coredumping, if you have a lot of test failures, may take a LOOONNNNGG time, and in at least one case has led to developers giving up on test suites. * the C/C++ standards say that it is illegal to redefine abort() to take less drastic action. It's common, yes, but not necessarily portable. Better to define your own assert-like macro, and allow it to be #defin'd to standard assert() if you wish. I prefer a generic name like TEST_ASSERT, rather than a specific name like CPPUNIT_ASSERT, since my tests usually are for libraries that may have to coexist with several different test suites. '''Printing the Assert Condition and Coordinates''' One nearly always wants to print a message like file.cc line 25: in function test_complex_numbers: assertion "Complex(1,2) == Complex(1,2)" failed The standard C/C++ __FILE__ and __LINE__ give you the filename and linenumber, allowing your IDE or GNU EMACS to jump to the failure. Unfortunately C/C++ do not have a standard way of obtaining the function name, although GNU C/C++ has __FUNCTION__ and __PRETTY_FUNCTION__, and many other compilers have __func__. Note that the fully adorned name may be desirable, since simply printing "test" rather than "TestComplexAdd.test()" is disappointing. The final part is typically the text of the assertion condition. In C/C++ this is typically obtained by a macro like (omitting the other coordinates for simplicity): #define TEST_ASSERT(cond) { if( !(cond) ) { printf("%s failed\n",##cond); } } // end macro TEST_ASSERT As a result, it is therefore traditional for asserts to be a macro. Unfortunately, this often produces macro wars, as the exact definition of the macro, whether it attempts to plug into a different test object, is up to question. '''TestAssert variants''' Simply printing the assert condition is not always the most effective message. For example retval_t ret = BigFunctionWithComplexReturn(); TEST_ASSERT( ret.code == 1 ); TEST_ASSERT( ret.vale = "1234" ); Therefore, several variants of assert are somewhat common: TEST_ASSERT_MSG( ret.code = 1, ("BigFunctionWithComplexReturn returned %d, expected 1\n",ret.code)) The reader may recognize the common C/C++ idiom of a parameterized argument for the printf-like string. Other variants include TEST_ASSERT_EQUALS( foo(), 1 ) producing a message like TEST_ASSERT_EQUALS failed: foo() == 55, expected 1 and variants for common things, like TEST_ASSERT_RANGE( fp_number, 1.5,.16) Although producing pleasant error messages, such variants quickly proliferate, and are not ubiquitous. They may hinder peaceful coexistence of your tests with a different test suite. '''Connecting TestAssert to the TestingFramework''' The test framework probably wants to do special things with the test assertions, including * changing output format * redirecting output * counting number of assertions run/passed/failed and so on. Redefining the TEST_ASSERT macro works, but note that you only get to do this once per compilation of the object files(s) that contain the TEST_ASSERTs. Therefore, you must arrange so that the definition of TEST_ASSERT does what you want. E.g. hardwiring output to cout rather than cerr is bad, if you may later want to have some tests that are expected to fail and whose output needs to be redirected to /dev/null. Therefore, typically TEST_ASSERT is #defined to call #define TEST_ASSERT(cond) {(if(!(cond)) ) tester.test_assert(__FILE__,__LINE__,##cond); }} ''Coords'' As you can see, the tester.test_assert() method must expect all of the arguments that are used to create meaningful coordinates. These may be factored out into another macro #ifdef GNU_C # define FILE_COORDS file_coords(__FILE__,__LINE,__FUNCTION__) #else # define FILE_COORDS file_coords(__FILE__,__LINE,"function name not available") #endif #define TEST_ASSERT(cond) {(if(!(cond)) ) tester.test_assert(FILE_COORDS,##cond); }} but this begins the slippery slope of depending on more and more stuff. ''Global tester'' When TEST_ASSERT is defined to call a test object #define TEST_ASSERT(cond) {(if(!(cond)) ) tester.test_assert(##cond); }} the object is, unfortunately, usually a global. Some test suites pass a tester object around, but that is framework specific. Better code, but harder to live with in a heterogeneous environment. So, fall back to making the global a delegator, overriding, when redirection is necessary. Care must be taken to restore the old delegate after an exception - the usual C++ idiom for resource allocation applies. The global tester object needs to have a unique and well known name. Also, there may arise initialization ordering issues. Therefore, using the C++ singleton function idiom is desirable. class test_c { static tester_c& delegate; public: virtual void set_delegate( tester_c& arg_delegate ) ... virtual void print( ... ) ... }; tester_c global_tester_singleton() { static tester_c tester; return tester; } ---- CategoryAssertions