The Problem
The code uses an enumeration (or other magic integer constants) as a return value. When these are used in tests, the CPPUNIT macros compare them as integers, and the output is not very clear.
Example
This enumeration is used to determine the time of day given the current time:
1 /* Here is the enumeration definition, from the header file */
2 enum TIME_OF_DAY {
3 TOD_NIGHT,
4 TOD_DAWN,
5 TOD_DAY,
6 TOD_DUSK,
7 TOD_LAST // This and higher are invalid values
8 };
9
10 /* Here is a function that returns the enumeration, from another source file */
11 TIME_OF_DAY get_time_of_day();
12
13 /* Here is a test for that function, from a different source file */
14 void TimeTests::testTimeOfDay()
15 {
16 CPPUNIT_ASSERT_EQUAL(TOD_DAY, get_time_of_day());
17 }
When the test fails, the text output is:
Test name: TimeTests::testTimeOfDay equality assertion failed - Expected: 2 - Actual : 0
Without going back to enumeration definition, it is hard to tell what "2" and "0" refer to.
Solution
Create a function that converts the enumeration to a string. For example:
1 /* This function can be declared in the same header as the enumeration */
2 #include <string>
3 const std::string TIME_OF_DAY_Name(const TIME_OF_DAY tod);
4
5 /* Here is a test of the new function */
6 void TimeTests::testTimeOfDayName()
7 {
8 CPPUNIT_ASSERT(std::string("Night"),TIME_OF_DAY_Name(TOD_NIGHT));
9 CPPUNIT_ASSERT_EQUAL(std::string("Day"), TIME_OF_DAY_Name(TOD_DAY));
10 CPPUNIT_ASSERT_EQUAL(std::string("Night"), TIME_OF_DAY_Name(TOD_NIGHT));
11 CPPUNIT_ASSERT_EQUAL(std::string("Dusk"), TIME_OF_DAY_Name(TOD_DUSK));
12 CPPUNIT_ASSERT_EQUAL(std::string("Dawn"), TIME_OF_DAY_Name(TOD_DAWN));
13 CPPUNIT_ASSERT_EQUAL(std::string("INVALID"), TIME_OF_DAY_Name(TOD_LAST));
14 CPPUNIT_ASSERT_EQUAL(
15 std::string("INVALID"),
16 TIME_OF_DAY_Name(TIME_OF_DAY(TOD_LAST+1)));
17 }
18
19 /* Here is a re-written version of the failing test */
20 void TimeTests::testTimeOfDay()
21 {
22 CPPUNIT_ASSERT_EQUAL(
23 TIME_OF_DAY_Name(TOD_DAY),
24 TIME_OF_DAY_Name(get_time_of_day()));
25 }
Here is the new (clearer) output for the failing test:
Test name: UtilTests::testTimeOfDayName equality assertion failed - Expected: Night - Actual : Day
Here are three possible implementations of that function. This first one uses a switch statement, so it works best if the number of enumeration values are small:
1 const std::string TIME_OF_DAY_Name(const TIME_OF_DAY tod)
2 {
3 switch (tod) {
4 case TOD_NIGHT: return std::string("Night");
5 case TOD_DAWN: return std::string("Dawn");
6 case TOD_DAY: return std::string("Day");
7 case TOD_DUSK: return std::string("Dusk");
8 default: return std::string("INVALID");
9 }
10 }
This implementation works well for large enumerations without gaps:
1 const std::string TIME_OF_DAY_Name(const TIME_OF_DAY tod)
2 {
3 const std::string invalid_name = "INVALID";
4 const std::string Names[] = {
5 "Night",
6 "Dawn",
7 "Day",
8 "Dusk",
9 invalid_name
10 };
11 if (tod < TOD_LAST) return Names[tod];
12 else return invalid_name;
13 }
This implementation works for large enumerations with gaps:
1 #include <map>
2 const std::string TIME_OF_DAY_Name(const TIME_OF_DAY tod)
3 {
4 typedef std::map<TIME_OF_DAY, std::string> MapType;
5 static MapType *the_map = 0;
6
7 if (!the_map) {
8 the_map = new MapType;
9 *the_map[TOD_NIGHT] = "Night";
10 *the_map[TOD_DAY] = "Day";
11 *the_map[TOD_DAWN] = "Dawn";
12 *the_map[TOD_DUSK] = "Dusk";
13 }
14
15 std::string name = *the_map[tod];
16 if (name.empty()) return "INVALID";
17 else return name;
18 }
This function will also be useful for any debug output using the TIME_OF_DAY enumeration.