私はこれが議論された慣行であることを知っていますが、これが私にとって最良の選択肢であるとしましょう。これを行う実際のテクニックは何なのかと思っています。私が見るアプローチはこれです:
1)テストしたいメソッドのクラスの友達クラスを作成します。
2)フレンドクラスで、テストしたクラスのプライベートメソッドを呼び出すパブリックメソッドを作成します。
3)フレンドクラスのパブリックメソッドをテストします。
上記の手順を説明する簡単な例を次に示します。
#include <iostream>
class MyClass
{
friend class MyFriend; // Step 1
private:
int plus_two(int a)
{
return a + 2;
}
};
class MyFriend
{
public:
MyFriend(MyClass *mc_ptr_1)
{
MyClass *mc_ptr = mc_ptr_1;
}
int plus_two(int a) // Step 2
{
return mc_ptr->plus_two(a);
}
private:
MyClass *mc_ptr;
};
int main()
{
MyClass mc;
MyFriend mf(&mc);
if (mf.plus_two(3) == 5) // Step 3
{
std::cout << "Passed" << std::endl;
}
else
{
std::cout << "Failed " << std::endl;
}
return 0;
}
編集:
次のディスカッションで、私のコードベースについて人々が疑問に思っていることがわかります。
私のクラスには、他のメソッドによって呼び出されるメソッドがあります。これらのメソッドはいずれもクラスの外部で呼び出すべきではないので、プライベートでなければなりません。もちろん、それらは1つの方法に入れることができますが、論理的にははるかに分離されています。これらのメソッドはユニットテストを保証するほど複雑であり、パフォーマンスの問題のため、これらのメソッドをリファクタリングする必要がある可能性が非常に高いため、リファクタリングによって何も壊されないことを確認するためのテストがあるといいでしょう。チームで作業しているのは私だけではありませんが、テストを含めてこのプロジェクトで作業しているのは私だけです。
上記のことを言っても、フィードバックはありがたいのですが、私の質問は、プライベートメソッドの単体テストを作成することは良い習慣かどうかではありませんでした。
私が頻繁に使用する(まあ、ある意味で)友人の代替手段は、access_byとして知られるようになったパターンです。とても簡単です:
class A {
void priv_method(){};
public:
template <class T> struct access_by;
template <class T> friend struct access_by;
}
ここで、クラスBがAのテストに関与しているとします。次のように記述できます。
template <> struct access_by<B> {
call_priv_method(A & a) {a.priv_method();}
}
次に、このaccess_byの特殊化を使用して、Aのプライベートメソッドを呼び出すことができます。基本的に、これにより、Aのプライベートメソッドを呼び出すクラスのヘッダーファイルに友情を宣言する義務が生じます。また、Aのソースを変更せずにAに友達を追加することもできます。慣用的には、Aのソースを読む人には、AがBのインターフェースを拡張するという意味での真の友だちではないことも示しています。むしろ、Aのインターフェースは与えられたとおり完全であり、BはAへの特別なアクセスを必要とします(テストは良い例です。ブーストpythonバインディングを実装するときにこのパターンを使用しました。 C++でプライベートである必要があるpythonレイヤー)に公開するには便利です。
テストするのが難しい場合は、正しく記述されていません
独自のテストを保証するのに十分なほど複雑なプライベートメソッドを持つクラスがある場合、そのクラスはあまりにも多くを実行しています。中には別のクラスが出ようとしています。
テストするプライベートメソッドを新しいクラスに抽出し、それらをパブリックにします。新しいクラスをテストします。
このリファクタリングにより、コードのテストが容易になるだけでなく、コードの理解と保守も容易になります。
プライベートメソッドをテストするべきではありません。限目。クラスを使用するクラスは、クラスが提供するメソッドのみを考慮し、内部で使用するクラスは考慮しません。
コードカバレッジについて心配している場合は、パブリックメソッド呼び出しの1つからそのプライベートメソッドをテストできる構成を見つける必要があります。それができない場合、そもそもメソッドを持っていることの意味は何ですか?それは単に到達できないコードです。
これを行うにはいくつかのオプションがありますが、それらは(本質的に)モジュールのパブリックインターフェイスを変更して、内部実装の詳細へのアクセスを提供することを覚えておいてください(ユニットテストを密接に結合されたクライアント依存関係に効果的に変換します) no依存関係はあります)。
テストしたクラスにフレンド(クラスまたは関数)宣言を追加できます。
テストしたコードを#define private public
- ingする前に、テストファイルの先頭に#include
を追加できます。ただし、テストされたコードが既にコンパイルされたライブラリである場合は、ヘッダーがすでにコンパイルされたバイナリコードと一致しなくなる可能性があります(UBが発生します)。
テストしたクラスにマクロを挿入し、後日そのマクロが何を意味するかを決定できます(テストコードの定義が異なります)。これにより内部をテストできますが、サードパーティのクライアントコードがクラスにハッキングすることもできます(追加した宣言で独自の定義を作成することにより)。
疑わしい質問に対する疑わしい提案を以下に示します。リリースされたコードはテストについて知っている必要があるため、友達のカップリングは好きではありません。 Nirの答えはそれを軽減する1つの方法ですが、テストに準拠するようにクラスを変更することはあまり好きではありません。
私は継承にあまり依存しないので、他の方法ではプライベートメソッドを保護し、必要に応じてテストクラスを継承および公開することもあります。実際には、パブリックAPIとテストAPIは異なる場合があり、プライベートAPIとは異なる場合があるため、バインドのようなものになります。
これは私がこのトリックに頼る種類の実用的な例です。私は埋め込みコードを書いており、ステートマシンにかなり依存しています。外部APIは、必ずしも内部のステートマシンの状態を知る必要はありませんが、テストでは、(おそらく)設計ドキュメントのステートマシン図への適合性をテストする必要があります。 「現在の状態」のゲッターを保護されているものとして公開し、テストへのアクセスを許可して、状態マシンをより完全にテストできるようにすることができます。このタイプのクラスは、ブラックボックスとしてテストするのが難しいことがよくあります。
友達を使う必要をなくすために、たくさんの回避策を使ってコードを書くことができます。
クラスを作成することができ、プライベートメソッドをまったく持つことはできません。次に必要なことは、コンパイルユニット内に実装関数を作成し、クラスがそれらを呼び出して、アクセスする必要のあるデータメンバーを渡すことです。
そして、はい、将来ヘッダーを変更せずに、署名を変更したり、新しい「実装」メソッドを追加したりできることを意味します。
それが価値があるかどうかにかかわらず、あなたは重さを量る必要があります。そして、多くのことは実際にあなたのヘッダーを見ることになる人に依存します。
サードパーティのライブラリを使用している場合、ユニットテスターへのフレンド宣言は表示しません。私も彼らのライブラリを構築したくないし、私がするときに彼らのテストを実行させたい。残念ながら、私が構築したサードパーティのオープンソースライブラリが多すぎて、それができません。
テストはライブラリの作成者の仕事であり、ユーザーではありません。
ただし、すべてのクラスがライブラリのユーザーに表示されるわけではありません。多くのクラスは「実装」であり、適切に動作することを保証するための最良の方法で実装します。それらでは、まだプライベートメソッドとメンバーを持っているかもしれませんが、ユニットテスターにそれらをテストしてもらいたいです。そのため、堅牢なコードをより迅速に生成できる場合は、そのようにしてください。そうする必要がある人にとっては、保守が簡単です。
クラスのユーザーがすべてあなたの会社またはチーム内にいる場合、会社のコーディング標準で許可されていると仮定して、その戦略についてもう少しリラックスすることもできます。