Blog Home

Data Driven Traps!

2008-09-04

Data driven tests are efficient, but easy to abuse.

Bad

struct TestData {
  const std::string word;
  const bool is_word;
};

const std::vector<TestData> test_data = {
    {"milk", true},
    {"centre", false},
    {"jklm", false},
};

TEST(IsWordTest, TestEverything) {
  for (const auto& entry : test_data) {
    EXPECT_EQ(IsWord(entry.word), entry.is_word);
  }
}

Data-driven tests make debugging and understanding failures, let alone false positives, more difficult.

As the code grows in complexity, data tends to grow even faster. It quickly becomes impossible to discern what behavior each piece of data is meant to test.

Bad

const std::vector<Locale> locales = { Word::US, Word::UK, Word::France, ... };

struct TestData {
  std::string word;
  bool[kNumLocales] is_word;
};

const std::vector<TestData> test_data = {
    {"milk", {true, true, false, ...},
    {"centre", {false, true, true, ...}},
    {"jklm", {false, false, false, ...}},
};

TEST(IsWordTest, TestEverything) {
  for (const auto& entry : test_data) {
    for (const auto* locale: locales) {
      EXPECT_EQ(IsWord(entry.word, locale), entry.is_word);
    }
  }
}

Instead, think critically about what behaviors are worth testing.

Good

TEST(IsWordTest, ShouldExistInMultipleLocales) {
  EXPECT_TRUE(IsWord("milk", Word::US));
  EXPECT_TRUE(IsWord("milk", Word::UK));
  EXPECT_FALSE(IsWord("milk", Word::France));
}

TEST(IsWordTest, ShouldNotExist) {
  // "jklm" test not repeated as it uses the same code path
  EXPECT_FALSE(IsWord("jklm", Word::US));
}