the Compartmented Robust Posix C++ Unit Test system

Chapter 3. Grouping tests into testsuites

It is often a good idea to group tests that verify similar parts of the functionality into testsuites. In crpcut a testsuite is a list of tests and enclosed testsuites. At the out most level, tests are included in the unnamed testsuite.

Testsuites are declared using the TESTSUITE(name, ...) macro. A testsuite is a C++ namespace with some decorations attached. For example it is possible to express dependencies on testsuites (i.e. the requirement that all tests in a testsuite pass,) and it is possible for testsuites to depend on tests and other testsuites.

[Tip]Tip
Experience has shown that it is a good idea to let testsuites depend on testsuites, and tests in testsuites depend on other tests in the same testsuite

Consider the simple symtable class from earlier:


     #include <map>
     #include <string>
     #include <cassert>
     
     class symtable
     {
     public:
       void add(const char *name, int val)
       {
         assert(name);
         table[name] = val;
       }
       int lookup(const char *name)
       {
         assert(name);
         return table.at(name);
       }
     private:
       std::map<std::string, int> table;
     };

The following longish test program follows the recommended form for testsuites with dependencies:


     #include "symtable.hpp"
     #include <crpcut.hpp>
     
     TESTSUITE(construct_and_destroy)
     {
       TEST(construct)
       {
         (void)new symtable; // intentional leak
       }
     
       TEST(destroy, DEPENDS_ON(construct))
       {
         symtable s;
       }
     }
     
     TESTSUITE(normal_access, DEPENDS_ON(ALL_TESTS(construct_and_destroy)))
     {
       TEST(insert_one)
       {
         symtable s;
         s.add("one", 1);
       }
       TEST(insert_several, DEPENDS_ON(insert_one))
       {
         symtable s;
         s.add("one", 1);
         s.add("two", 2);
         s.add("three", 3);
       }
       TEST(lookup, DEPENDS_ON(insert_several))
       {
         symtable s;
         s.add("one", 1);
         s.add("two", 2);
         int v = s.lookup("one");
         ASSERT_EQ(v, 1);
         v = s.lookup("two");
         ASSERT_EQ(v, 2);
       }
     }
     
     TESTSUITE(abnormal, DEPENDS_ON(ALL_TESTS(normal_access)))
     {
       TEST(lookup_nonexisting, EXPECT_EXCEPTION(std::out_of_range))
        {
          symtable s;
          s.add("one", 1);
          s.lookup("two");
        }
     
       TEST(add_null, EXPECT_SIGNAL_DEATH(SIGABRT), NO_CORE_FILE)
       {
         symtable s;
         s.add(0, 1);
       }
     
       TEST(lookup_null, EXPECT_SIGNAL_DEATH(SIGABRT), NO_CORE_FILE)
       {
         symtable s;
         s.add("one", 1);
         s.lookup(0);
       }
     }
     
     int main(int argc, char *argv[])
     {
       return crpcut::run(argc, argv);
     }

Testsuites are also useful when there is a desire to run a subset of the tests. Running the above test program with -v -n "normal_access" yields:


     PASSED: normal_access::insert_one
     ===============================================================================
     PASSED: normal_access::insert_several
     ===============================================================================
     PASSED: normal_access::lookup
     ===============================================================================
     Total 3 test cases selected
     UNTESTED : 0
     PASSED   : 3
     FAILED   : 0

(the -n command line flag tells crpcut to ignore dependencies, and the -v command line flag makes crpcut list also the tests that succeeds.)