Automated Unit Testing On-The-Cheap: Part 2
C++ Code Capsules
In Part 1 of this two-part series I introduced a time-tested (i.e., old :-)) technique that handled automated unit testing in a remarkably simple way, including validating proper exception handling. The previous post left two problems on the table, however, and the journey to fix them turns out to be a nice tour of two key features of modern C++: inline variables and modules.
The simplicity of the test framework discussed in Part 1 follows from everything being contained in a small header file, test.h (include guards not shown):
namespace { std:: size_t nPass = 0 ; size_t nPass std:: size_t nFail = 0 ; size_t nFail inline void do_fail ( const char * text , const char * fileName , long lineNumber ) { do_failtextfileNamelineNumber std:: cout << "FAILURE: " << text << " in file " << fileName couttextfileName << " on line " << lineNumber << std:: endl ; lineNumberendl ++ nFail ; nFail } inline void do_test ( const char * condText , bool cond , const char * fileName , long lineNumber ) { do_testcondTextcondfileNamelineNumber if (! cond ) cond ( condText , fileName , lineNumber ); do_failcondTextfileNamelineNumber else ++ nPass ; nPass } inline void succeed_ () noexcept { ++ nPass ; nPass } inline void report_ () { std:: cout << "
Test Report:
" ; cout std:: cout << " \t Number of Passes = " << nPass << std:: endl ; coutnPassendl std:: cout << " \t Number of Failures = " << nFail << std:: endl ; coutnFailendl } } #define test_ ( cond ) do_test (# cond , cond , __FILE__ , __LINE__ ) conddo_testcondcond #define fail_ ( expr ) do_fail ( expr , __FILE__ , __LINE__ ) exprdo_failexpr #define throw_ ( expr , T ) \ expr try { \ expr ; \ expr std:: cout << "THROW " ; \ cout do_fail (# expr , __FILE__ , __LINE__ ); \ do_failexpr } catch ( const T &) { \ ++ nPass ; \ nPass } catch (...) { \ std:: cout << "THROW " ; \ cout do_fail (# expr , __FILE__ , __LINE__ ); \ do_failexpr } #define nothrow_ ( expr ) \ expr try { \ expr ; \ expr ++ nPass ; \ nPass } catch (...) { \ std:: cout << "NOTHROW " ; \ cout do_fail (# expr , __FILE__ , __LINE__ ); \ do_failexpr }
Generally users only have to call the test_ macro, which captures the expression being tested as text along with its associated file name and line number. For example, if a source line is
test_ ( stk . top () == 1 ); stktop
... continue reading