A test passes. Great! But does it really mean your code is working as expected? Not necessarily.Sometimes the values you choose in your tests can create a false sense of security, especially when dealing with default values.
Consider this snippet of a simple map class and its corresponding unit test:
Implementation
Test
void MyMap::insert(int key, int value) {
// Oops! The map entry is default-initialized,
// the second parameter is not used.
internal_map_[key];
}
TEST(MyMapTest, Insert) {
MyMap my_map;
my_map.insert(1, 0);
// This passes!
EXPECT_EQ(my_map.get(1), 0);
The test passes, but the insert method is broken! It never actually stores the value. The test only passes because the default value for an integer in the map (0) happens to match the value used in the test.
When choosing test values, consider the following:
Test with non-default values. Explicitly test with values different from the type's default (e.g., non-zero numbers, non-empty strings, enum values other than the one at index 0). This provides greater confidence that your code is actually using the provided input.
my_map.insert(1, 5);
// This test would fail and reveal the bug in
// the implementation above: “Expected 5, got 0”.
EXPECT_EQ(my_map.get(1), 5);
Test multiple inputs that cover different scenarios, where it is reasonable to do so.
Consider empty/missing/null values, numerical boundaries, and special cases that trigger complex logic. Try to cover all distinct code/logic paths.
Consider using fuzzing to more thoroughly cover the input domain.
Use different values for each input. This guarantees the code under test doesn't accidentally reuse a single input or switch their order. Parameterized testing can also help test a large variety of inputs with minimal code duplication.
// Use a different value for `key` and `value`.
my_map.insert(/*key=*/1, /*value=*/2);
EXPECT_EQ(my_map.at(1), 2);