私は この投稿 プライベートメソッドのテスト方法について読みました。通常、オブジェクトの外部から呼び出されるパブリックメソッドのみをテストする方が速いと思ったため、通常はテストしません。プライベートメソッドをテストしますか?常にテストする必要がありますか?
私はプライベートメソッドをユニットテストしません。プライベートメソッドは、クラスのユーザーに非表示にする必要がある実装の詳細です。プライベートメソッドをテストすると、カプセル化が壊れます。
プライベートメソッドが巨大または複雑であるか、独自のテストを必要とするほど重要であることがわかった場合は、別のクラスに入れてそこで公開します( Method Object )。次に、以前はプライベートだったが現在は公開されているメソッドを、独自のクラスで簡単にテストできます。
テストの目的は何ですか?
これまでの回答の大半は、プライベートメソッドは実装の詳細であり、パブリックインターフェイスが十分にテストされて機能している限り、重要ではない(少なくとも重要ではない)と言っています。それは絶対に正しいですテストの唯一の目的がパブリックインターフェイスの動作を保証することである場合。
個人的には、コードテストの主な用途は、将来のコード変更が問題を引き起こさないことを確認し、問題が発生した場合のデバッグ作業を支援することです。私は、パブリックインターフェイスと同じくらい徹底的にプライベートメソッドをテストすると(そうでない場合でも!)、その目的が促進されることに気付きました。
考えてみてください:プライベートメソッドBを呼び出すパブリックメソッドAがあります。AとBは両方ともメソッドCを使用します。 AがCを使用しているのか、BがCを使用しているのか、またはその両方に問題があるのかがわかるように、Bのテストもプライベートではありますが便利ではないでしょうか
プライベートメソッドのテストは、パブリックインターフェイスのテストカバレッジが不完全な場合にも価値を高めます。これは一般的に回避したい状況ですが、効率の単体テストは、バグを検出するテストと、それらのテストに関連する開発および保守コストの両方に依存します。場合によっては、100%のテストカバレッジの利点がこれらのテストのコストを保証するには不十分であると判断され、パブリックインターフェイスのテストカバレッジにギャップが生じることがあります。このような場合、プライベートメソッドのターゲットを絞ったテストは、コードベースに非常に効果的に追加できます。
私は彼らの本でデイブ・トーマスとアンディ・ハントのアドバイスに従う傾向がありますPragmatic Unit Testing:
一般的に、テストのためにカプセル化を解除したくない(または、お母さんが「プライベートを公開しないで!」と言っていたように)。ほとんどの場合、クラスのパブリックメソッドを実行することでクラスをテストできるはずです。プライベートまたは保護されたアクセスの背後に重要な機能が隠されている場合、それは脱出に苦労している別のクラスがあるという警告サインかもしれません。
しかし、私は完全堅牢なプログラムを構築しているという安心感を与えてくれるので、プライベートメソッドのテストを止められないことがあります。
私たちのプロジェクトで私たちの最新のQA勧告の1つをますますフォローしているので、私はプライベート機能をテストすることを余儀なくされています。
関数ごとに 循環的複雑度 で10以下。
現在、このポリシーの実施の副作用は、私の非常に大きなパブリック関数の多くが、より焦点の合った、より適切な名前のprivate関数に分割されることです。
パブリック関数は(もちろん)そこにありますが、本質的にこれらすべてのプライベートな「サブ関数」と呼ばれるようになります
コールスタックは読みやすくなっているため、これは実際にはクールです(大きな関数内のバグではなく、コールスタック内の前の関数の名前を持つサブサブ関数にバグがあるため、理解しやすくなっています) 「どうやってそこに着いた」)
ただし、これらのprivate関数を直接単体テストし、大規模なパブリック関数のテストを何らかの「統合」テストに任せる方が簡単になりましたシナリオに対処する必要がある場所。
ちょうど私の2セント。
はい。プライベート関数はパブリックメソッドでテストされますが、アプリケーションの最小部分をテストするのはTDD(Test Driven Design)であるためです。ただし、テストユニットクラスにいる場合、プライベート関数にはアクセスできません。プライベートメソッドをテストするために行うことは次のとおりです。
なぜプライベートメソッドがあるのですか?
プライベートメソッドは、パブリックメソッドで読み取り可能なコードを作成するため、主にクラスに存在します。このクラスのユーザーがこれらのメソッドを直接呼び出すのではなく、パブリックメソッドを介して呼び出します。また、クラスを拡張するとき(保護されている場合)に動作を変更したくないため、プライベートです。
コーディング時には、テスト駆動設計(TDD)を使用します。これは、プライベートな機能につまずいて、テストしたいことがあることを意味します。プライベート関数はphpUnitでテストできません。Testクラスでアクセスできないためです(プライベート関数です)。
ここに3つのソリューションがあると思います:
1。パブリックメソッドを使用してプライベートをテストできます
利点
欠点
2。プライベートが非常に重要な場合、多分それのための新しい別個のクラスを作成するのはコードメルです
利点
欠点
。アクセス修飾子を(最終)保護に変更
利点
欠点
例
class Detective {
public function investigate() {}
private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
public function investigate() {}
final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {
public test_sleepWithSuspect($suspect)
{
//this is now accessible, but still not overridable!
$this->sleepWithSuspect($suspect);
}
}
そのため、テストユニットはtest_sleepWithSuspectを呼び出して、以前のプライベート関数をテストできます。
いくつかの理由でプライベート機能をテストするのは好きではありません。それらは次のとおりです(これらはTLDRの人々にとっての主要なポイントです):
これらのそれぞれを具体的な例で説明します。 2)と3)はやや複雑につながっているため、例は似ていますが、プライベートメソッドをテストしない理由は別だと考えています。
プライベートメソッドのテストが適切な場合がありますが、上記の欠点を認識することが重要です。後で詳しく説明します。
最後に、TDDがプライベートメソッドをテストするための有効な言い訳ではない理由についても説明します。
私が見る最も一般的な(アンチ)パターンの1つは、Michael Feathers呼び出し「Iceberg」クラス(Michael Feathersが誰なのかわからない場合は、「レガシーコードを効果的に使用する」という本を購入/読んでください。彼は、あなたがプロのソフトウェアエンジニア/開発者であるかどうかを知る価値があります)。この問題を引き起こす他の(アンチ)パターンがありますが、これは私が偶然見つけた最も一般的なものです。 「Iceberg」クラスには1つのパブリックメソッドがあり、残りはプライベートです(そのため、プライベートメソッドをテストするのは魅力的です)。通常、1つのパブリックメソッドが呼び出されるため、「Iceberg」クラスと呼ばれますが、残りの機能はプライベートメソッドの形で水中に隠されています。次のようになります。
たとえば、GetNextToken()
を文字列で連続して呼び出して、期待される結果が返されることを確認して、テストすることができます。このような関数はテストを保証します。特にトークン化ルールが複雑な場合、その動作は簡単ではありません。それほど複雑ではないふりをしましょう。スペースで区切られたトークンでロープを張るだけです。したがって、テストを作成すると、おそらく次のようになります(一部の言語に依存しない擬似コード、アイデアが明確であることが望ましい)。
TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
input_string = "1 2 test bar"
re = RuleEvaluator(input_string);
ASSERT re.GetNextToken() IS "1";
ASSERT re.GetNextToken() IS "2";
ASSERT re.GetNextToken() IS "test";
ASSERT re.GetNextToken() IS "bar";
ASSERT re.HasMoreTokens() IS FALSE;
}
まあ、それは実際にはかなり素敵に見えます。変更を行ったときにこの動作を維持することを確認したいと思います。しかし、GetNextToken()
はprivate関数です!そのため、このようにテストすることはできませんコンパイルさえしないので(Pythonのようないくつかのスクリプト言語とは異なり、実際にパブリック/プライベートを強制する言語を使用していると仮定)しかし、単一責任の原則(単一責任の原則)に従うようにRuleEvaluator
クラスを変更するのはどうでしょうか。たとえば、パーサー、トークナイザー、およびエバリュエーターが1つのクラスに詰め込まれているようです。これらの責任を分離する方が良いと思いませんか?さらに、Tokenizer
クラスを作成すると、そのパブリックメソッドはHasMoreTokens()
およびGetNextTokens()
になります。 RuleEvaluator
クラスには、Tokenizer
オブジェクトをメンバーとして含めることができます。これで、Tokenizer
クラスの代わりにRuleEvaluator
クラスをテストすることを除いて、上記と同じテストを維持できます。
UMLでは次のようになります。
この新しい設計によりモジュール性が向上するため、システムの他の部分でこれらのクラスを再利用できる可能性があることに注意してください(定義できない場合、プライベートメソッドは再利用できません)。これは、RuleEvaluatorを分類することの主な利点であり、理解度/局所性が向上します。
テストは非常によく似ていますが、GetNextToken()
メソッドがTokenizer
クラスでパブリックになったため、今回は実際にコンパイルされる点が異なります。
TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
input_string = "1 2 test bar"
tokenizer = Tokenizer(input_string);
ASSERT tokenizer.GetNextToken() IS "1";
ASSERT tokenizer.GetNextToken() IS "2";
ASSERT tokenizer.GetNextToken() IS "test";
ASSERT tokenizer.GetNextToken() IS "bar";
ASSERT tokenizer.HasMoreTokens() IS FALSE;
}
問題をより少ないモジュラーコンポーネントに分割できると思わない場合でも(tryを行うだけで95%の時間になります)、単純にパブリックインターフェイスを介してプライベート機能をテストします。多くの場合、プライベートメンバーはパブリックインターフェイスを介してテストされるため、テストする価値はありません。私が見るものの多くは、見た目は非常に似ているテストですが、2つの異なる関数/メソッドをテストしています。最終的に起こるのは、要件が変更されると(常に変更される)、1ではなく2つのテストが壊れていることです。そして、すべてのプライベートメソッドを実際にテストした場合、1ではなく10のテストが壊れている可能性があります要するに、パブリックインターフェイスを介してテストできないプライベート関数(FRIEND_TEST
を使用するか、パブリックにするかリフレクションを使用する)をテストすると、テストの重複が発生する可能性があります。テストスイートがあなたを遅くする以上に痛いものはないので、あなたは本当にこれを望んでいません。開発時間を短縮し、メンテナンスコストを削減することになっています!他の方法でパブリックインターフェイスを介してテストされるプライベートメソッドをテストする場合、テストスイートはその逆を非常にうまく実行し、メンテナンスコストと開発時間を積極的に増やします。プライベート関数を公開するとき、またはFRIEND_TEST
やリフレクションのようなものを使用する場合、通常は長期的には後悔することになります。
Tokenizer
クラスの次の可能な実装を検討してください。
SplitUpByDelimiter()
は、配列内の各要素がトークンであるような配列を返す役割を担っているとしましょう。さらに、GetNextToken()
はこのベクトルの単なる反復子であるとだけ言ってみましょう。したがって、公開テストは次のようになります。
TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
input_string = "1 2 test bar"
tokenizer = Tokenizer(input_string);
ASSERT tokenizer.GetNextToken() IS "1";
ASSERT tokenizer.GetNextToken() IS "2";
ASSERT tokenizer.GetNextToken() IS "test";
ASSERT tokenizer.GetNextToken() IS "bar";
ASSERT tokenizer.HasMoreTokens() IS false;
}
Michael Featherが模索ツールと呼んでいるふりをしましょう。これは、他の人のプライベートな部分に触れることができるツールです。例はgoogletestのFRIEND_TEST
、または言語がサポートしている場合はリフレクションです。
TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
input_string = "1 2 test bar"
tokenizer = Tokenizer(input_string);
result_array = tokenizer.SplitUpByDelimiter(" ");
ASSERT result.size() IS 4;
ASSERT result[0] IS "1";
ASSERT result[1] IS "2";
ASSERT result[2] IS "test";
ASSERT result[3] IS "bar";
}
さて、要件が変更され、トークン化がはるかに複雑になったとしましょう。単純な文字列区切り文字では十分ではないと判断し、ジョブを処理するためにDelimiter
クラスが必要です。当然ながら、1つのテストが失敗することを期待しますが、プライベート関数をテストするとその痛みが増します。
ソフトウェアには「万能」というものはありません。 「規則を破る」ことは大丈夫です(実際には理想的です)。できる限りプライベート機能をテストしないことを強く推奨します。大丈夫だと思うとき、主に2つの状況があります。
私はレガシシステムで広範囲に取り組んできました(だからこそ、私はマイケルフェザーズの大ファンです)。プライベート機能をテストすることが最も安全であると言うことができます。 「特性評価テスト」をベースラインに入れるのに特に役立ちます。
あなたは急いでいる、そして今ここで可能な限り最速のことをしなければならない。長い目で見れば、プライベートメソッドをテストする必要はありません。しかし、通常、設計の問題に対処するにはリファクタリングに時間がかかります。また、場合によっては1週間以内に発送する必要があります。それは大丈夫です。それがあなたが仕事を成し遂げるための最も速くて最も信頼できる方法であると思うなら、手探りツールを使用して迅速かつ汚いことを行い、プライベートメソッドをテストします。しかし、あなたがやったことは長期的には最適ではないことを理解し、それに戻ってくることを検討してください(または忘れてしまったのに後で見る場合は修正してください)。
おそらく他の状況でも大丈夫です。大丈夫だと思い、正当な理由があるなら、それをしてください。誰もあなたを止めません。潜在的なコストに注意してください。
余談ですが、私は、TDDをプライベートメソッドのテストの口実として使用している人が本当に好きではありません。私はTDDを実践しており、TDDフォースは考えていませんあなたがこれを行う。最初に(パブリックインターフェイス用の)テストを記述してから、そのインターフェイスを満たすコードを記述できます。パブリックインターフェイスのテストを作成することもありますが、1つまたは2つの小さなプライベートメソッドを作成することでも満足できます(ただし、プライベートメソッドを直接テストすることはありませんが、それらが機能するか、パブリックテストが失敗することはわかっています) )。そのプライベートメソッドのエッジケースをテストする必要がある場合は、パブリックインターフェイスを介してヒットするテストの束を作成します。 Edgeケースをヒットする方法がわからない場合、これは、それぞれ独自のパブリックメソッドを持つ小さなコンポーネントにリファクタリングする必要がある強力な兆候です。これは、プライベート関数があまりにも多くのことをしていて、クラスの範囲外であるというサインです。
また、時々、噛むには大きすぎるテストを書いていることに気付くので、「ええ、作業するAPIが増えたら、後でそのテストに戻る」と思います(私はそれをコメントアウトして、私の心の奥に置いておきます)。ここで、私が出会った多くの開発者が、TDDをスケープゴートとして使用して、プライベート機能のテストの作成を開始します。 「まあ、他のテストが必要ですが、そのテストを書くには、これらのプライベートメソッドが必要です。したがって、テストを書かずに本番コードを書くことはできないので、テストを書く必要があります。プライベートメソッドの場合。」しかし、彼らが本当にしなければならないことは、現在のクラスにたくさんのプライベートメソッドを追加/テストする代わりに、小さくて再利用可能なコンポーネントにリファクタリングすることです。
注意:
GoogleTestを使用したプライベートメソッドのテスト に関する同様の質問に少し前に答えました。ここでは、ほとんどの言語にとらわれないようにその答えをほとんど修正しました。
追伸マイケルフェザーズによる氷山のクラスと模索ツールに関する関連する講義は次のとおりです。 https://www.youtube.com/watch?v=4cVZvoFGJT
オブジェクトのパブリックインターフェイスをテストすることをお勧めします。外の世界の観点からは、パブリックインターフェイスの動作のみが重要であり、これがユニットテストの対象となります。
オブジェクトのソリッドユニットテストを作成したら、インターフェイスの背後にある実装が変更されたからといって、それらのテストに戻って変更する必要はありません。この状況では、単体テストの一貫性が損なわれています。
プライベートメソッドがパブリックメソッドを呼び出してテストされていない場合、何をしていますか?私は保護されていない、または友人ではないプライベートを話している。
プライベートメソッドが適切に定義されている場合(つまり、テスト可能な機能があり、時間の経過とともに変化することを意図していない場合)、はい。理にかなっている場合は、テスト可能なすべてをテストします。
たとえば、暗号化ライブラリは、一度に8バイトのみを暗号化するプライベートメソッドでブロック暗号化を実行するという事実を隠す場合があります。私はそのための単体テストを書くでしょう-それは隠されていても、変更するつもりはありませんし、(たとえば、将来のパフォーマンス強化のために)壊れた場合、それが壊れたのはプライベート関数であるだけでなく、パブリック関数の1つが壊れた。
後でデバッグを高速化します。
-アダム
私はこの分野の専門家ではありませんが、単体テストでは実装ではなく動作をテストする必要があります。プライベートメソッドは厳密に実装の一部であるため、IMHOはテストしないでください。
推論によってプライベートメソッドをテストします。つまり、少なくとも95%のクラステストの合計カバレッジを探しますが、テストはパブリックメソッドまたは内部メソッドのみを呼び出します。カバレッジを取得するには、発生する可能性のあるさまざまなシナリオに基づいて、パブリック/内部に対して複数の呼び出しを行う必要があります。これにより、テスト対象のコードの目的に関するテストがより意図的になります。
リンクした投稿に対するTrumpiの答えは最高です。
テスト駆動(TDD)を開発している場合、プライベートメソッドをテストします。
私が信じる単体テストは、パブリックメソッドをテストするためのものです。パブリックメソッドはプライベートメソッドを使用するため、間接的にもテストされています。
私はこの問題をしばらくの間、特にTDDで試してみました。
TDDの場合、この問題に十分に対処すると思う2つの投稿に出会いました。
まとめ:
テスト駆動開発(設計)手法を使用する場合、プライベートメソッドは、既に動作してテスト済みのコードのリファクタリングプロセス中にのみ発生します。
プロセスの性質上、徹底的にテストされた機能から抽出された単純な実装機能は、自己テストされます(つまり、間接的なテスト範囲)。
私にとって、コーディングの最初の部分では、ほとんどのメソッドが設計をカプセル化/記述しているため、より高レベルの関数になることは十分に明らかです。
したがって、これらのメソッドは公開され、テストは簡単になります。
プライベートメソッドは、すべてが正常に機能するようになったら後で登場し、読みやすさとクリーンさのためにリファクタリングしています。
するべきではない。プライベートメソッドに十分な複雑さがあり、テストする必要がある場合は、別のクラスに配置する必要があります。 high cohesionを維持し、クラスには1つの目的のみが必要です。クラスのパブリックインターフェイスで十分なはずです。
上記で引用したように、「プライベートメソッドをテストしない場合、それらが壊れないことをどのように確認しますか?」
これは大きな問題です。単体テストの大きなポイントの1つは、どこで、いつ、どのように何かがすぐに壊れたのかを知ることです。したがって、開発とQAの労力を大幅に削減できます。テストされているものがすべて公開されている場合、クラスの内部を正直にカバーし、描写することはできません。
これを行う最良の方法の1つは、プロジェクトにテスト参照を追加し、プライベートメソッドと並行してクラスにテストを配置することです。テストが最終プロジェクトに組み込まれないように、適切なビルドロジックを配置します。
そうすれば、これらの方法をテストすることのすべての利点が得られ、数分または数時間ではなく数秒で問題を見つけることができます。
要約すると、はい、プライベートメソッドを単体テストします。
プライベートメソッドをテストしない場合、それらが壊れないことをどのように確認しますか?
明らかに言語に依存しています。以前、c ++を使用して、テストクラスをフレンドクラスとして宣言しました。残念ながら、これにはテストクラスを知るために実稼働コードが必要です。
私は、プライベートメソッドが実装の詳細と見なされ、テストする必要がないという観点を理解しています。オブジェクトの外部でのみ開発する必要がある場合は、この規則を守ります。しかし、私たちは、オブジェクトの外部でのみ開発し、パブリックメソッドのみを呼び出している、ある種の制限された開発者ですか?それとも、実際にそのオブジェクトを開発していますか?オブジェクトの外部のプログラムにバインドされていないため、おそらくこれらのプライベートメソッドを、開発中の新しいパブリックメソッドに呼び出す必要があります。プライベートメソッドがあらゆる可能性に抵抗することを知るのは素晴らしいことではないでしょうか。
私たちがそのオブジェクトに別のパブリックメソッドを開発している場合、これをテストする必要があると答える人がいることを知っています(プライベートメソッドはテストなしで生き続けることができます)。しかし、これはオブジェクトのすべてのパブリックメソッドにも当てはまります。Webアプリを開発するとき、オブジェクトのすべてのパブリックメソッドはコントローラーメソッドから呼び出されるため、コントローラーの実装の詳細と見なすことができます。
では、なぜオブジェクトの単体テストを行うのですか?基盤となるコードのすべてのブランチをトリガーする適切な入力を使用してコントローラーのメソッドをテストしていることを確認することは不可能ではないのではなく、本当に難しいためです。言い換えれば、スタックの上位にあるほど、すべての動作をテストすることは難しくなります。プライベートメソッドでも同じです。
私にとって、プライベートメソッドとパブリックメソッドの境界は、テストに関しては心理的な基準です。私にとって重要な基準は次のとおりです。
プライベートメソッドが巨大または複雑であるか、独自のテストを必要とするほど重要であることがわかったら、それを別のクラスに入れてそこで公開します(メソッドオブジェクト)。それから、以前はプライベートでしたが、現在は独自のクラスに存在するパブリックメソッドを簡単にテストできます。
パブリックとプライベートは、テストから呼び出すAPIの有用な区別ではなく、メソッドとクラスの区別もありません。ほとんどのテスト可能なユニットは、あるコンテキストでは表示されますが、他のコンテキストでは表示されません。
重要なのは、補償範囲と費用です。プロジェクトのカバレッジ目標(ライン、ブランチ、パス、ブロック、メソッド、クラス、同等クラス、ユースケース...チームが決定するものは何でも)を達成しながら、コストを最小限に抑える必要があります。
したがって、ツールを使用してカバレッジを確保し、テストを設計してコストが最小になるようにします(短期および長期)。
テストを必要以上に高価にしないでください。パブリックエントリポイントのみをテストするのが最も安価な場合は、そうします。プライベートメソッドをテストするのが最も安価な場合は、それを行います。
経験を積むにつれて、テストメンテナンスの長期的なコストを回避するために、リファクタリングする価値がある時期を予測する能力が向上します。
「プライベートメソッドをテストする必要がありますか?」に対する回答「.......時々」です。通常、クラスのインターフェイスに対してテストする必要があります。
以下に例を示します。
class Thing
def some_string
one + two
end
private
def one
'aaaa'
end
def two
'bbbb'
end
end
class RefactoredThing
def some_string
one + one_a + two + two_b
end
private
def one
'aa'
end
def one_a
'aa'
end
def two
'bb'
end
def two_b
'bb'
end
end
RefactoredThing
には5つのテストがあり、そのうち2つはリファクタリングのために更新する必要がありましたが、オブジェクトの機能は実際には変更されていません。したがって、物事はそれよりも複雑であり、次のような出力の順序を定義するメソッドがあるとしましょう。
def some_string_positioner
if some case
elsif other case
elsif other case
elsif other case
else one more case
end
end
これは外部ユーザーによって実行されるべきではありませんが、カプセル化クラスは、何度も何度もそのロジックを実行するために重いかもしれません。この場合、多分あなたはむしろこれを別のクラスに抽出し、そのクラスにインターフェースを与え、それに対してテストするでしょう。
そして最後に、メインオブジェクトが非常に重く、メソッドが非常に小さく、出力が正しいことを確認する必要があるとしましょう。 「このプライベートメソッドをテストする必要があります!」と考えています。重い作業の一部を初期化パラメーターとして渡すことで、オブジェクトをより軽くできるかもしれませんか?次に、より軽いものを渡して、それに対してテストすることができます。
それは、パブリックまたはプライベートのメソッドまたは関数だけでなく、実装の詳細に関するものです。プライベート関数は、実装の詳細の1つの側面にすぎません。
ユニットテストは、結局のところ、ホワイトボックステストのアプローチです。たとえば、カバレッジ分析を使用して、これまでのテストで無視されていたコードの部分を特定する人は、実装の詳細に入ります。
A)はい、実装の詳細をテストする必要があります。
パフォーマンス上の理由で、最大10個の要素がある場合はBubbleSortのプライベート実装を使用し、10個を超える要素がある場合は異なるソートアプローチ(ヒープソートなど)のプライベート実装を使用するソート関数を考えます。パブリックAPIはソート関数です。ただし、テストスイートは、実際には2つのソートアルゴリズムが使用されているという知識を活用します。
この例では、確かに、パブリックAPIでテストを実行できます。ただし、これには、ヒープソートアルゴリズムが十分にテストされるように、10個を超える要素でソート関数を実行する多くのテストケースが必要です。このようなテストケースだけが存在するということは、テストスイートが関数の実装の詳細に接続されていることを示しています。
並べ替え関数の実装の詳細が変更された場合、2つの並べ替えアルゴリズム間の制限が変更されたり、ヒープソートがmergesortなどに置き換えられたりする可能性があります。既存のテストは引き続き機能します。それにもかかわらず、それらの値は疑わしく、変更されたソート関数をより良くテストするために再加工する必要があります。言い換えると、テストがパブリックAPIで行われたという事実にもかかわらず、メンテナンス作業が行われます。
B)実装の詳細をテストする方法
多くの人がプライベート関数や実装の詳細をテストすべきではないと主張する理由の1つは、実装の詳細が変更される可能性が高いことです。少なくともこの変更の可能性が高いことが、インターフェースの背後に実装の詳細を隠す理由の1つです。
ここで、インターフェイスの背後にある実装に、内部インターフェイスでの個別のテストがオプションとなる可能性のある、より大きなプライベート部分が含まれると仮定します。一部の人々は、これらの部分はプライベートのときにテストされるべきではなく、パブリックなものに変換されるべきだと主張します。パブリックになったら、そのコードの単体テストは問題ありません。
これは興味深いです。インターフェースは内部でしたが、実装の詳細であるため、変更される可能性がありました。同じインターフェイスを使用して公開すると、魔法の変換が行われます。つまり、変更の少ないインターフェイスに変換されます。この議論には明らかに欠陥があります。
しかし、それにも関わらずいくつかの真実があります。特に内部インターフェイスを使用して、実装の詳細をテストするときは、安定したままである可能性が高いインターフェイスを使用するよう努力する必要があります。ただし、一部のインターフェースが安定している可能性が高いかどうかは、それがパブリックであるかプライベートであるかに基づいて単純に決定できるわけではありません。私が以前から取り組んできた世界のプロジェクトでは、パブリックインターフェイスも十分に変更されることが多く、プライベートインターフェイスの多くは何年も変更されていません。
それでも、「玄関先から」を使用するのが良い経験則です( http://xunitpatterns.com/Principles%20of%20Test%20Automation.html を参照)。ただし、「玄関先のみ」ではなく「玄関先」と呼ばれることに注意してください。
C)まとめ
実装の詳細もテストします。安定したインターフェイス(パブリックまたはプライベート)でのテストをお勧めします。実装の詳細が変更された場合は、パブリックAPIのテストも修正する必要があります。プライベートなものをパブリックに変えても、その安定性は魔法のように変わりません。
メソッドをパッケージプライベートにする、つまりデフォルトにすることもできます。プライベートにする必要がない限り、ユニットテストを行うことができます。
1つの主なポイントは
ロジックの正確性を確認するためにテストし、プライベートメソッドがロジックを保持している場合、テストする必要があります。そうじゃない?では、なぜそれをスキップするのでしょうか?
メソッドの可視性に基づいてテストを記述することは、まったく無関係な考えです。
逆に
一方、元のクラスの外部でプライベートメソッドを呼び出すことが主な問題です。また、一部のモッキングツールにはプライベートメソッドをモックする制限があります。 (例:Mockito)
Power Mockのようないくつかのツールがありますが、これは危険な操作です。その理由は、それを実現するためにJVMをハックする必要があるからです。
実行可能な回避策の1つは(プライベートメソッドのテストケースを作成する場合)
これらのprivateメソッドをprotectedとして宣言します。ただし、いくつかの状況では便利ではない場合があります。
メソッドが十分に重要/複雑な場合、通常は「保護」してテストします。一部のメソッドはプライベートのままにされ、public/protectedメソッドの単体テストの一部として暗黙的にテストされます。
はい、可能な限りプライベートメソッドをテストする必要があります。どうして?不要な 状態空間の爆発 テストケースを回避するために、最終的には同じ入力で繰り返し同じプライベート関数を暗黙的にテストすることになります。例を挙げて理由を説明しましょう。
次の少し不自然な例を考えてみましょう。 3つの整数を受け取り、それらの3つの整数がすべて素数である場合にのみtrueを返す関数を公開したいとします。次のように実装します。
public bool allPrime(int a, int b, int c)
{
return andAll(isPrime(a), isPrime(b), isPrime(c))
}
private bool andAll(bool... boolArray)
{
foreach (bool b in boolArray)
{
if(b == false) return false;
}
return true;
}
private bool isPrime(int x){
//Implementation to go here. Sorry if you were expecting a prime sieve.
}
ここで、パブリック関数のみをテストするという厳密なアプローチを採用する場合、allPrime
またはisPrime
ではなく、andAll
のみをテストできます。
テスターとして、各引数の5つの可能性に興味があるかもしれません:< 0
、= 0
、= 1
、prime > 1
、not prime > 1
。しかし、徹底するためには、引数のすべての組み合わせがどのように連携するかを確認する必要があります。つまり、5*5*5
= 125のテストケースであるため、直感に従ってこの関数を徹底的にテストする必要があります。
一方、プライベート関数のテストを許可されていれば、より少ないテストケースで同じくらいの範囲をカバーできます。 isPrime
を以前の直感と同じレベルまでテストするために必要なテストケースは5つだけです。そして、ダニエル・ジャクソンによって提案された 小範囲仮説 によって、私たちは、小さな長さまでandAll
関数をテストする必要があるだけです。 3または4。最大16個のテストになります。合計で21のテスト。もちろん、125の代わりに、おそらくallPrime
に対してfewテストを実行したいと思うでしょうが、そうする義務はありません。気にしたと言った入力シナリオの125の組み合わせすべてを網羅的にカバーします。ほんの少しの幸せな道。
確かに不自然な例ですが、明確なデモンストレーションのために必要でした。そして、そのパターンは実際のソフトウェアにまで及びます。プライベート関数は通常、最低レベルの構築ブロックであるため、多くの場合、一緒に組み合わされて高レベルのロジックを生成します。高いレベルでの意味は、さまざまな組み合わせのために、下位レベルのものの繰り返しが多くなります。
私は多くの人々が同じ考え方であると思います:公開レベルでテストします。しかし、それは私たちのQAチームが行うことではありませんか?入力と期待される出力をテストします。開発者としてパブリックメソッドのみをテストする場合は、QAのジョブをやり直し、「ユニットテスト」によって値を追加するのではありません。
いいえプライベートメソッドをテストしないでください 理由? さらに、Mockitoなどの一般的なモックフレームワークはプライベートメソッドのテストをサポートしていません。
私は単体テストの概念を理解することはありませんが、今ではそれが目的であることを知っています。
単体テストは完全なテストではありません。したがって、QAと手動テストの代わりになるものではありません。この側面でのTDDの概念は、プライベートメソッドだけでなく、リソース(特に制御できないリソース)を使用するメソッドを含むすべてをテストすることはできないため、間違っています。 TDDはその品質のすべてを達成できなかったものです。
単体テストは、より多くのピボットテスト任意のピボットをマークすると、ピボットの結果は同じままになります。