the Compartmented Robust Posix C++ Unit Test system |
|
---|
It is not uncommon for several tests to work from a configuration that is common to all of them. Duplicating the same setup in each test is not only boring, it is a hallmark sign of poor software implementation, and also in itself error prone. A convenient way of doing away with the repetition is to use test fixtures.
A fixture is in itself nothing but a class (or struct,) which does
all the configuration in the default constructor and, if necessary,
does cleanup in the destructor. By adding the name of the fixture
class (or struct) after the test name in the
TEST(name, ...)
definition, the
fixture is inherited by the test class. Public or protected
data and member functions in the fixture are available directly
in the test body. It is possible to list several fixtures
in the same TEST(name, ...)
definition,
in which case multiple inheritance is used.
Fixtures and
modifiers can be mixed in any order.
The simple symtable, below, was introduced on previous page on “Testsuites”:
#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; };
Some of the tests can be rewritten with fixtures to avoid duplication:
#include "symtable.hpp" #include <crpcut.hpp> class have_symtable { protected: symtable s; }; class stdinsert : public have_symtable { protected: stdinsert() { s.add("one", 1); s.add("two", 2); } }; class nullinsert : public have_symtable { protected: nullinsert() { s.add(0, 1); } }; TEST(insert_and_lookup, stdinsert) { ASSERT_EQ(s.lookup("one"), 1); ASSERT_EQ(s.lookup("two"), 2); } TEST(lookup_nonexisting, stdinsert, EXPECT_EXCEPTION(std::out_of_range)) { s.lookup("three"); } TEST(add_null, nullinsert) { } TEST(lookup_null, stdinsert, EXPECT_SIGNAL_DEATH(SIGABRT), NO_CORE_FILE) { s.lookup(0); } int main(int argc, char *argv[]) { return crpcut::run(argc, argv); }
Running this alternative test program yields:
FAILED!: add_null stderr------------------------------------------------------------------------- symtable-test2: samples/symtable.hpp:36: void symtable::add(const char*, int): Assertion `name' failed. ------------------------------------------------------------------------------- /tmp/crpcutu4ybWu/add_null is not empty! phase="creating" ------------------------------------------------------------- samples/symtable-test2.cpp:66 Died with core dump ------------------------------------------------------------------------------- =============================================================================== Files remain under /tmp/crpcutu4ybWu 4 test cases selected Sum Critical Non-critical PASSED : 3 3 0 FAILED : 1 1 0
Something new here is the
phase=creating
on
the error report for add_null. The phase
can take the values:
creating
The error was detected when running the fixture constructors.
running
The error was detected when running the test function body.
destroying
The error was detected when running the fixture destructors.
post_mortem
The error was detected after the death of the test process.
child
The error was detected in a child process spawned from the test case. The entire process group is killed.
Tip | |
---|---|
Use fixtures templated on type to express parametrized tests
and fixtures templated on references to globals to fake passing
values to fixture constructors.
#include <crpcut.hpp> #include <string> template <const char *(&str_param)> class fix { protected: fix() : string(str_param) {} std::string string; }; void function_working_with_strings(const std::string &s) { INFO << s; } const char *hello = "Hello string fixture"; const char *other = "Fresh out of cheddar"; TEST(func_with_hello, fix<hello>) { function_working_with_strings(string); } TEST(func_with_other, fix<other>) { function_working_with_strings(string); } int main(int argc, char *argv[]) { return crpcut::run(argc, argv); }Running the program with the -v / --verbose command
line flag yields:
PASSED!: func_with_hello info--------------------------------------------------------------------------- samples/string-template-fix.cpp:41 Hello string fixture =============================================================================== PASSED!: func_with_other info--------------------------------------------------------------------------- samples/string-template-fix.cpp:41 Fresh out of cheddar =============================================================================== 2 test cases selected Sum Critical Non-critical PASSED : 2 2 0 |