web-dev-qa-db-ja.com

TDDを実行する場合、プライベートメソッドを避ける必要がありますか?

私は今TDDを学んでいます。私の理解では、プライベートメソッドはテスト不可能であり、心配する必要はありません。パブリックAPIがオブジェクトの整合性を検証するための十分な情報を提供するからです。

私はOOP=をしばらく理解しました。プライベートメソッドはオブジェクトをよりカプセル化するため、変更やエラーに対してより耐性があることを理解しています。したがって、これらはデフォルトで使用されるべきメソッドとクライアントへの問題は公開されるべきです。

ええと、私はプライベートメソッドのみを持ち、イベントをリッスンすることによって他のオブジェクトと対話するオブジェクトを作成することが可能です。これは非常にカプセル化されますが、完全にテストできません。

また、テストのためにメソッドを追加することは悪い習慣と見なされています。

これは、TDDがカプセル化と対立していることを意味しますか?適切なバランスとは何ですか?私は自分のメソッドのほとんどまたはすべてを今公開する傾向があります...

106
pup

実装のテストよりもインターフェイスよりもテストを優先します。

プライベートメソッドはテストできないというのが私の理解です

これは開発環境によって異なります。以下を参照してください。

[プライベートメソッド]は心配する必要はありません。パブリックAPIがオブジェクトの整合性を検証するための十分な情報を提供するためです。

そうです、TDDはインターフェースのテストに焦点を合わせています。

プライベートメソッドは、リファクタリングサイクル中に変更される可能性のある実装の詳細です。インターフェースやblack-box動作を変更せずにリファクタリングできるはずです。実際、それはTDDの利点の一部であり、クラスの内部の変更がそのクラスのユーザーに影響を与えないという確信を簡単に生成できることです。

ええと、私はプライベートメソッドのみを持ち、イベントをリッスンすることによって他のオブジェクトと対話するオブジェクトを作成することが可能です。これは非常にカプセル化されますが、完全にテストできません。

クラスにpublicメソッドがない場合でも、イベントハンドラーはpublic interfaceであり、それに対してthatテストできるパブリックインターフェイス

イベントはインターフェースなので、そのオブジェクトをテストするために生成する必要があるのはイベントです。

mock objects をテストシステムのglueとして使用することを検討してください。イベントを生成し、結果として生じる状態の変化(別のレシーバーモックオブジェクトによって可能)を取得する単純なモックオブジェクトを作成することが可能でなければなりません。

また、テストのためにメソッドを追加することは悪い習慣と見なされます。

絶対に、内部状態を公開することに非常に注意する必要があります。

これは、TDDがカプセル化と対立していることを意味しますか?適切なバランスとは何ですか?

絶対違う。

TDDは、おそらくそれらを簡略化する以外にクラスの実装を変更すべきではありません(以前のポイントから [〜#〜] yagni [〜#〜] を適用することによって)。

TDDを使用する場合のベストプラクティスは、TDDを使用しない場合のベストプラクティスと同じです。開発中にインターフェイスを使用しているため、理由がすぐにわかります。

私は今、自分のメソッドのほとんどまたはすべてを公開する傾向があります...

これはむしろ、赤ちゃんを風呂の水で捨てることです。

TDDの方法で開発できるように、すべてのメソッドをパブリックにするneedするべきではありません。以下の私のメモを参照して、プライベートメソッドが本当にテストできないかどうかを確認してください。

プライベートメソッドのテストの詳細

言語/環境に応じて、クラスのプライベート動作を絶対にユニットテストする必要がある場合、次の3つのオプションがあります。

  1. テストするクラスにテストを配置します。
  2. テストを別のクラス/ソースファイルに配置し、テストするプライベートメソッドをパブリックメソッドとして公開します。
  3. テストコードと本番コードを分離したまま、テストコードで本番コードのプライベートメソッドにアクセスできるテスト環境を使用します。

明らかに、3番目のオプションが最善です。

1)テストするクラスにテストを配置します(理想的ではありません)

テスト対象の本番コードと同じクラス/ソースファイルにテストケースを保存するのが最も簡単なオプションです。しかし、プリプロセッサディレクティブやアノテーションが多くなければ、テストコードが本番用コードを不必要に肥大化し、コードの構成方法によっては、誤って内部実装をそのコードのユーザーに公開してしまう可能性があります。

2)テストするプライベートメソッドをパブリックメソッドとして公開します(実際に良い方法ではありません)。

これは非常に貧弱な習慣であることを示唆しているように、カプセル化を破壊し、内部実装をコードのユーザーに公開します

3)より良いテスト環境を使用します(可能な場合は最良のオプション)

Eclipseの世界では、3。は fragments を使用して実現できます。 C#の世界では、 部分クラス を使用できます。他の言語/環境も同様の機能を備えていることがよくあります。それを見つけるだけです。

盲目的に1.または2.しかないと仮定すると、テストコードまたは汚いリネンを公共の場で洗い流す厄介なクラスインターフェイスで生産ソフトウェアが肥大化する可能性があります。 * 8 ')

  • 結局のところ、プライベートな実装に対してテストしない方がはるかに優れています。
55
Mark Booth

もちろん、プライベートメソッドを使用することも、テストすることもできます。

プライベートメソッドを実行するsome方法があります。その場合、その方法でテストできます。または、プライベートを実行するno方法があります。どちらの場合:一体それをテストしようとしているのか、いまいましいものを削除してください!

あなたの例では:

ええと、私はプライベートメソッドのみを持ち、イベントをリッスンすることによって他のオブジェクトと対話するオブジェクトを作成することが可能です。これは非常にカプセル化されますが、完全にテストできません。

なぜそれがテストできないのでしょうか?イベントに応じてメソッドが呼び出される場合は、テストでオブジェクトに適切なイベントをフィードするだけです。

それはプライベートメソッドを持たないことではなく、カプセル化を壊さないことです。プライベートメソッドを使用できますが、パブリックAPIを使用してテストする必要があります。パブリックAPIがイベントに基づいている場合は、イベントを使用します。

プライベートヘルパーメソッドのより一般的なケースでは、それらを呼び出すパブリックメソッドを通じてテストできます。特に、失敗したテストに合格するためのコードを書くことだけが許可されており、テストはパブリックAPIをテストしているため、通常、作成するすべての新しいコードはパブリックになります。プライベートメソッドは、既存のパブリックメソッドからプルされたとき、Extract Method Refactoringの結果としてのみ表示されます。しかしその場合、パブリックメソッドがプライベートメソッドを呼び出すため、パブリックメソッドをテストする元のテストはプライベートメソッドもカバーします。

したがって、通常、プライベートメソッドは、既にテスト済みのパブリックメソッドから抽出された場合にのみ表示されるため、すでにテスト済みでもあります。

76
Jörg W Mittag

コードで新しいクラスを作成するとき、いくつかの要件に答えるためにそれを行います。 要件はwhatではなく、コードが実行する必要があるhowです。これにより、ほとんどのテストがパブリックメソッドレベルで行われる理由を簡単に理解できます。

テストを通じて、コードが期待どおりの動作をすることを確認します、予期したときに適切な例外をスローするなど開発者がコードをどのように実装するかは、特に気にしません。実装、つまりコードがどのように機能するかは気にしませんが、プライベートメソッドのテストを避けることは理にかなっています。

パブリックメソッドがなく、イベントを介してのみ外部の世界とやり取りするクラスのテストについては、テストを介してイベントを送信し、応答をリッスンすることによってもテストできます。たとえば、クラスがイベントを受信するたびにログファイルを保存する必要がある場合、ユニットテストはイベントを送信し、ログファイルが書き込まれたことを確認します。

最後に重要なことですが、プライベートメソッドをテストすることが完全に有効な場合もあります。そのため、たとえば.NETでは、ソリューションがパブリックメソッドほど簡単ではない場合でも、パブリッククラスだけでなくプライベートクラスもテストできます。

26

プライベートメソッドはテストできないというのが私の理解です

私はそのステートメントに同意しません、または私はあなたがプライベートメソッドをテストしないと言います直接。パブリックメソッドは、さまざまなプライベートメソッドを呼び出すことができます。たぶん、作者は「小さな」メソッドを持ちたいと思って、コードの一部を巧妙に名付けられたプライベートメソッドに抽出しました。

Publicメソッドの記述方法に関係なく、テストコードはすべてのパスをカバーする必要があります。テスト後に、1つのプライベートメソッドの分岐ステートメント(if/switch)の1つがテストでカバーされていないことがわかった場合は、問題があります。ケースを見逃して実装が正しいOR実装が間違っており、そのブランチが実際に存在することはなかったはずです。

そのため、私はCoberturaとNCoverを頻繁に使用して、私のパブリックメソッドテストがプライベートメソッドもカバーしていることを確認しています。 OOプライベートメソッドを含むオブジェクトを自由に記述し、TDD /テストをそのような問題に陥らせないでください。

6
Jalayn

依存性注入を使用して、CUTが対話するインスタンスを提供する限り、サンプルは引き続き完全にテスト可能です。次に、モックを使用して、目的のイベントを生成し、CUTがその依存関係に対して正しいアクションを実行するかどうかを確認できます。

一方、イベントサポートが充実した言語を使用している場合は、少し異なる方法をとることがあります。オブジェクトがイベント自体をサブスクライブするときは気に入らず、代わりに、オブジェクトを作成するファクトリに、イベントをオブジェクトのパブリックメソッドにワイヤリングします。テストが簡単になり、CUTをテストする必要のあるイベントの種類を外部から確認できるようになります。

5
Chris Pitman

プライベートメソッドを使用して放棄する必要はありません。それらを使用することは完全に合理的ですが、テストの観点から、カプセル化を壊したり、クラスにテスト固有のコードを追加したりしないと、直接テストすることは困難です。トリックは、コードを汚したように感じるので、あなたが腸のうねりを作ろうとしていることがわかっているものを最小限にすることです。

これらは、実行可能なバランスを実現するために私が心がけていることです。

  1. 使用するプライベートメソッドとプロパティの数を最小限に抑えます。とにかくクラスに必要なもののほとんどは公開する必要がある傾向があるので、その賢いメソッドを本当にプライベートにする必要があるかどうかを考えてください。
  2. プライベートメソッド内のコードの量を最小限に抑えます(とにかくこれを行う必要があります)。他のメソッドの動作を介して、間接的にテストできます。 100%のテストカバレッジを期待することは決してなく、おそらくデバッガを介していくつかの値を手動でチェックする必要があります。プライベートメソッドを使用して例外をスローすると、間接的に簡単にテストできます。プライベートプロパティは、手動または別の方法でテストする必要がある場合があります。
  3. 間接または手動のチェックがうまくいかない場合は、保護されたイベントを追加し、インターフェイスを介してアクセスして、プライベートなものの一部を公開します。これにより、カプセル化のルールが効果的に「曲げ」られますが、テストを実行するコードを実際に出荷する必要がなくなります。欠点は、必要なときにイベントが発生することを確認するための内部コードが少し追加される可能性があることです。
  4. Publicメソッドでは十分に「安全」ではないと思われる場合は、メソッドにある種の検証プロセスを実装して、それらの使用方法を制限できる方法があるかどうかを確認してください。あなたがこれを考えている間に、メソッドを実装するより良い方法を考えるか、別のクラスが形になり始めるのを見ることでしょう。
  5. パブリックメソッドの「処理」を行うプライベートメソッドがたくさんある場合は、抽出されるのを待っている新しいクラスがある可能性があります。これを個別のクラスとして直接テストできますが、それを使用するクラス内でプライベートにコンポジットとして実装できます。

横向きに考えます。クラスを小さくし、メソッドを小さくして、多くの構成を使用します。それはより多くの仕事のように聞こえますが、最終的にはより個別にテスト可能な項目が得られ、テストはよりシンプルになり、実際の大きくて複雑なオブジェクトの代わりにシンプルなモックを使用するためのオプションが増えるでしょう。因数分解され、疎結合されたコード、さらに重要なことに、より多くのオプションを自分に与えます。物事を小さく保つことは、各クラスで個別にチェックする必要のあるものの数を減らし、クラスが大きくなり、多くの場合に時々発生する可能性があるコードスパゲッティを自然に減らす傾向があるため、最終的には時間を節約する傾向があります。内部的に相互依存するコードの動作。

5
S.Robins

ええと、私はプライベートメソッドのみを持ち、イベントをリッスンすることによって他のオブジェクトと対話するオブジェクトを作成することが可能です。これは非常にカプセル化されますが、完全にテストできません。

このオブジェクトはこれらのイベントにどのように反応しますか?おそらく、他のオブジェクトのメソッドを呼び出す必要があります。これらのメソッドが呼び出されるかどうかを確認することでテストできます。それがモックオブジェクトを呼び出すようにすれば、期待通りの動作をすることを簡単に主張できます。

問題は、オブジェクトと他のオブジェクトとの相互作用のみをテストすることです。オブジェクトの内部で何が起こっているかは気にしません。したがって、以前はこれ以上パブリックメソッドを使用するべきではありません。

4
Winston Ewert

私はこれと同じ問題にも苦労しました。本当に、それを回避する方法はこれです:プログラムの残りの部分がそのクラスとインターフェースすることをどのように期待しますか?それに応じてクラスをテストします。これにより、プログラムの残りの部分がクラスとどのようにインターフェースするかに基づいてクラスを設計する必要があり、実際、クラスのカプセル化と適切な設計が促進されます。

4
Chance

プライベート使用の代わりにデフォルト修飾子。次に、パブリックメソッドと組み合わせるだけでなく、それらのメソッドを個別にテストできます。これには、テストがメインコードと同じパッケージ構造を持つ必要があります。

3
siamii

通常、いくつかのプライベートメソッドは問題になりません。コードがパブリックメソッドにインライン化されているかのように、パブリックAPIを介してテストするだけです。プライベートメソッドの過剰mayは、凝集度が低いことを示しています。クラスは1つのまとまりのある責任を持つ必要があり、多くの場合、実際には存在しない場所にまとまりのある外観を与えるために、メソッドをプライベートにします。

たとえば、これらのイベントに応答して多くのデータベース呼び出しを行うイベントハンドラーがあるとします。イベントハンドラーをインスタンス化してデータベース呼び出しを行うのは明らかに悪い習慣であるため、データベースに関連するすべての呼び出しをプライベートメソッドにして、それらを実際に別のクラスに引き出す必要がある場合があります。

2
Karl Bielefeldt

これは、TDDがカプセル化と対立していることを意味しますか?適切なバランスとは何ですか?私は現在、自分のメソッドのほとんどまたはすべてを公開する傾向があります。

TDDはカプセル化と対立しません。選択した言語に応じて、ゲッターメソッドまたはプロパティの最も単純な例を取り上げます。たとえば、Customerオブジェクトがあり、それにIdフィールドが必要だとします。最初に書くテストは、「customer_id_initializes_to_zero」のようなものです。実装されていない例外をスローするようにゲッターを定義し、テストの失敗を監視します。次に、そのテストに合格するために私ができる最も簡単なことは、ゲッターがゼロを返すようにすることです。

そこから、私は他のテストに進みます。おそらく、顧客IDが実際の機能的なフィールドであるテストです。ある時点で、ゲッターから返されるものを追跡するためにカスタマークラスが使用するプライベートフィールドを作成する必要があるでしょう。これを正確に追跡するにはどうすればよいですか?単純なバッキングintですか?文字列を追跡し、それをintに変換しますか? 20 intを追跡して平均化しますか?外の世界は気にしない-そしてあなたのTDDテストは気にしない。 カプセル化詳細です。

TDDを開始するときに、これが必ずしもすぐに明らかであるとは限らないと思います。つまり、メソッドが内部で実行することをテストするのではなく、クラスの細かい懸念をテストするのではありません。したがって、そのメソッドDoSomethingToFoo()をテストしてバーをインスタンス化したり、メソッドを呼び出したり、プロパティの1つに2つを追加したりすることはありません。テストするのは、オブジェクト、一部の状態のアクセサーが変更されました(または変更されません)。これがテストの一般的なパターンです。「テスト中のクラスにXを実行すると、その後Yを観察できます」。 Yに到達する方法はテストの問題ではありません。これはカプセル化されるものであり、これがTDDがカプセル化と対立しない理由です。

2
Erik Dietrich

使用を避ける?番号。
回避(で始まる?はい。

TDDを使用した抽象クラスを作成しても問題ないかどうかは質問していません。 TDD中に抽象クラスがどのように出現するかを理解している場合、同じ原則がプライベートメソッドにも適用されます。

プライベートメソッドを直接テストできないのと同じように、抽象クラスのメソッドを直接テストすることはできませんが、それが抽象クラスとプライベートメソッドから始めない理由です。具体的なクラスとパブリックAPIから始め、次に、一般的な機能をリファクタリングします。

2
user34530

クラスには要件があります。大まかに言えば、クラスには、要件に対応するパブリックメソッドがあり、他のメソッドはないはずです。

そして、あなたのクラスは要件を実装するためのコードを必要とします。要件によっては、多くのコードが必要になる場合があります。また、さまざまな理由から、メソッドが大きすぎてはいけません。

プライベートメソッドを要求しない場合は、すべてのコードをパブリックメソッドに配置する必要があります。したがって、適切なコードを含むクラスに必要な数だけ、プライベートメソッドを追加します。テスト容易性がすべてではありません。実際、優れたコーディング原則に違反していて、テストに合格するには常にバグが多いコードになってしまうと、テスト容易性は何にもなりません。

メソッドは、すべての古いコードがそれらを呼び出すのを防ぐためにプライベートにされています。これは、テストしたくないという意味ではありません。たとえば、MacOS/iOSでは、メソッドを@testableとしてマークするだけで、単体テストやその他のテストを構築するときはパブリックメソッドのように呼び出すことができますが、アプリケーションを構築するときは呼び出せません。

0
gnasher729