Blog Home

Avoiding Friend Twister in C++

2007-10-30

"Testing private members requires more friend contortions than a game of Twister®."

If you find yourself saying that, theres a better way.

Bad

// include/my_project/dashboard.h

class Dashboard {
 private:
  // Declaration of functions getResults(), GetResultsFromCache(),
  // GetResultsFromDatabase(), and CountPassFail()

  std::unique_ptr<Database> database_; // instantiated in constructor

  friend class DashboardTest; // one friend declaration per test fixture
};

Instead, make a helper class by extracting a helper class (a variant of the Pimple idiom).

To preserve privacy, the helper class is tucked away in a private implementation directory separate from the public API.

Good

// include/my_project/dashboard.h

class ResultsLog; // Foreword declare extracted helper interface

class Dashboard {
 public:
  explicit Dashboard(std::unique_ptr<ResultsLog> results)
      : results_(std::move(results)) {}

 private:
   std::unique_ptr<ResultsLog> results_;
};

// src/results_log.h

class ResultsLog {
 public:
  // Declaration of functions getResults(), GetResultsFromCache(),
  // GetResultsFromDatabase(), and CountPassFail()
};

// src/live_results_log.h

class LiveResultsLog : public ResultsLog {
 public:
  explicit LiveResultsLog(std::unique<Database> database)
      : database_(std::move(database)) {}
};

As an added bonus, now you can inject a MockResultsLog or a FakeDatabase for testing the Dashboard class.