プライベート/保護されたメソッドを単体テストする必要がありますか?


82

TDD開発では、通常、最初に行うことは、インターフェイスを作成してから、そのインターフェイスに対する単体テストの作成を開始することです。TDDプロセスを進めると、インターフェイスを実装するクラスが作成され、ある時点で単体テストに合格することになります。

ここで私の質問は、インターフェイスによって公開されるメソッド/プロパティをサポートするためにクラスに記述しなければならない可能性のあるプライベートメソッドと保護されたメソッドについてです。

  • クラスのプライベートメソッドには独自の単体テストが必要ですか?

  • クラス内の保護されたメソッドには、独自の単体テストが必要ですか?

私の考え:

  • 特に私はインターフェースにコーディングしているので、保護された/プライベートなメソッドはブラックボックスなので心配する必要はありません。

  • 私はインターフェースを使用しているので、定義されたコントラクトがインターフェースを実装するさまざまなクラスによって適切に実装されていることを検証するための単体テストを作成しています。したがって、プライベート/保護されたメソッドについて心配する必要はなく、それらを呼び出す単体テストを介して実行する必要があります。インターフェイスによって定義されたメソッド/プロパティ。

  • コードカバレッジでプロテクト/プライベートメソッドがヒットしていることが示されない場合は、適切な単体テストがないか、使用されていないため削除する必要のあるコードがあります。


1
テストから保護されたメソッドをオーバーライドするか、呼び出すことによって実行しない場合、なぜそれらはプライベートではなく保護されているのですか?それらを保護することにより、拡張ポイント/機能を公開するという意識的な決定を下しています。私にとって、TDDをフォローしている場合、この決定は、作成しているテストによって決定されるはずです。
forsvarir 2011

2
あなたは別の答えにあなた自身の考えについての部分を置くべきです。あなたがそうするとき私に知らせてください、そして私は賛成します。
キースピンソン


アクティブな単体テスト、つまり継続的に実行するように設定されている単体テストについては正しいです。これらの場合、パブリックインターフェイスと保護されたインターフェイスのみをテストする必要があります。プライベートメソッドのテストを作成することで、メリットを得ることができます。これらのテストは継続的なスイートの一部であってはなりませんが、実装が適切であることを確認するための1回限りのテストとして、非常に価値のあるツールになる可能性があります。
Didier A.

回答:


108

いいえ、プライベートメソッドや保護されたメソッドをテストすることは考えていません。クラスのプライベートメソッドと保護されたメソッドはパブリックインターフェイスの一部ではないため、パブリックの動作を公開しません。通常、これらのメソッドは、テストを緑色にした後に適用するリファクタリングによって作成されます。

したがって、これらのプライベートメソッドは暗黙的にテストされます、パブリックインターフェイスの動作をアサートするテストによってます。

より哲学的な注意として、メソッドではなく動作をテストしていることを忘れないでください。したがって、テスト対象のクラスが実行できる一連のことを考えると、クラスが期待どおりに動作することをテストおよびアサートできる限り、実装するためにクラスによって内部的に使用されるプライベート(および保護された)メソッドがあるかどうかその振る舞いは無関係です。これらのメソッドは、パブリック動作の実装の詳細です。


23
ユニットテストはメソッドではなく動作をテストすると言ったという事実が好きです!それは物事を非常に明確にします。
Raj Rao

1
@rajahに同意します。これは、すべてのチュートリアルの最初のステートメントである必要があります。私は自分のメソッドをテストする方法を考えていましたが、今ではテストする必要がないことがわかりました。+1
冷ややかな2011

3
これは、基本クラスが一般の人々が継承して使用することが期待される保護された動作を実装する場合にも当てはまると思いますか?それでも、保護されたメソッドはまだパブリックインターフェイスの一部ですよね?
Nick Udell 2014年

1
一般的に言えば、関心の分離を支持するパターンは分離された単体テストに適していますが、カプセル化を支持するパターンはAPIを使用する方が簡単です。
尤川豪2015

3
これは、保護された可視性のケースをクリアしません。保護されたメソッドもインターフェースの一部であるように思われます。多くの場合、それは拡張ポイントであり、意図的にそのように保護されています。そのような場合は、ユニットテストも行う必要があります。将来、誰かが物事を変更したり、動作の拡張ポイントに依存していたクラスを壊したりすることは望ましくありません。
ディディエA.

45

私はほとんどのポスターに同意しません。

最も重要なルールは次のとおりです。作業コードは、パブリック/保護/プライベートに関する理論上のルールを打ち負かします。

コードを徹底的にテストする必要があります。保護された/プライベートなメソッドを十分に実行するパブリックメソッドのテストを作成することでそこに到達できれば、それは素晴らしいことです。

できない場合は、できるようにリファクタリングするか、保護/プライベートルールを曲げます。

子供たちにテストを与えた心理学者についての素晴らしい話があります。彼は各子供に両端にロープが付いた2枚の木の板を渡し、できるだけ早く床に足を触れずに部屋を横切るように頼んだ。すべての子供たちは、小さなスキー板のようにボードを使用し、各ボードに片足を置き、ロープでそれらを保持し、床を横切ってスライドしました。それから彼は彼らに同じ仕事を与えましたが、1枚のボードだけを使用しました。彼らは、シングルボードの両端に片足で床を横切って旋回/「歩き」ました-そして彼らはより速くなりました!

Java(または任意の言語)に機能(プライベート/保護/パブリック)があるからといって、それを使用するために必ずしもより良いコードを書いているとは限りません!

これで、この競合を最適化/最小化する方法が常にあります。ほとんどの言語では、メソッドを(パブリックではなく)保護し、テストクラスを同じパッケージ(またはその他)に入れることができ、メソッドをテストに使用できるようになります。他のポスターで説明されているように、役立つ注釈があります。リフレクションを使用して、プライベートメソッドを取得できます(yuck)。

コンテキストも重要です。外部の人が使用するAPIを作成している場合は、パブリック/プライベートの方が重要です。それが内部プロジェクトの場合、誰が本当に気にしますか?

しかし、結局のところ、テストの欠如によって引き起こされたバグの数を考えてみてください。次に、「過度に目に見える」方法によって引き起こされたバグの数を比較します。その答えがあなたの決断を後押しするはずです。


3
メソッドが重要で、ロジックが複雑な場合、その動作を表明することはバグを防ぐのに非常に役立ちます。このようなメソッドの単体テストを作成すると、ある種の探索的な方法でメソッドを実装する場合にも役立ちます。したがって、プライベートであっても、単体テストの価値があると思います。しかし、大きな問題がありますが、テストはコード結合であることを覚えておく必要があります。メソッドにテストを書き込むと、リファクタリングが妨げられます。
ディディエA.

6
したがって、プライベートメソッドのテストを作成する前に、常に設計を再考する必要があります。物事を一般化して純粋関数型メソッドに変換できるかどうかを確認してください。もしそうなら、あなたはそれらを彼ら自身の構成に抽出することができます。このコンストラクトは、独自のパブリックインターフェイスを持ち、単体テストを行うことができます。多くの場合、プライベートメソッドの複雑な動作は、クラスが複数の責任を持っていることを示している可能性があることを忘れないでください。ですから、まずはデザインを考え直してください。
ディディエA.

ええ、でも「作業コード」とは何ですか?プライベートメソッドをテストしても、オブジェクトが正しい動作をするかどうかについては何もわかりません。これが、パブリックメソッドのみをテストする理由の主なポイントです。パブリックメソッドのみが、コードのユーザーが気にする動作を示します。
Sammi 2016年

1
「動作コード」は動作するコードです。プライベート(または準プライベート)メソッドにバグがあり、それがパブリックメソッドのテストで検出されない場合は、何か問題があります。たぶんあなたのデザインは間違っていて、十分に公平です:私は最良の解決策がパブリックメソッドを呼び出すテストであることに同意します。しかし、特にレガシーコードを追加または修正する場合は、それが常に可能であるとは限りません。(私は経験から、100万行のコードを含むプロジェクトについて話します。)テストされたコードは、テストされていないコードよりも常に優れています。パブリックメソッドのテストのみに関する優れたルールに違反したとしても!
チャールズロス

「テストはコード結合です...リファクタリングを防ぐ」に関するビット(上部)は100%間違っています。アーキテクチャの比喩では、テストは具体的なものではなく、足場です。物事は変化し、テストは変化し、捨てられ、新しいテストが書かれます。優れた設計により、テストの書き換えが最小限に抑えられることに同意します。しかし、最高のデザインであっても、変化は起こります。
チャールズロス

34

あなたが書いた:

TDD開発では、通常、最初に行うことは、インターフェイスを作成してから、そのインターフェイスに対する単体テストの作成を開始することです。TDDプロセスを進めると、インターフェイスを実装するクラスが作成され、ある時点で単体テストに合格することになります。

これをBDD言語で言い換えさせてください。

クラスが価値がある理由とその動作を説明するとき、通常最初に行うことは、多くの場合そのインターフェイス*を介してクラスを使用する方法の例を作成することです。目的の動作を追加すると、その値を提供するクラスが作成され、ある時点で例が機能します。

*Interfaceクラスの実際のAPIまたは単にアクセス可能なAPIである可能性があります。例:Rubyにはインターフェースがありません。

これが、プライベートメソッドをテストしない理由です。テストはクラスの使用方法の例であり、実際に使用することはできないためです。必要に応じてできることは、プライベートメソッドの責任を共同作業するクラスに委任してから、そのヘルパーをモック/スタブすることです。

保護されたメソッドを使用すると、クラスを拡張するクラスには特定の動作があり、何らかの値を提供する必要があるということです。次に、クラスの拡張機能を使用して、その動作を示すことができます。たとえば、順序付けられたコレクションクラスを作成している場合、同じ内容の2つの拡張機能が同等であることを示したい場合があります。

お役に立てれば!


1
素晴らしい投稿。多くを明確にします。
frostymarvelous 2011

17

クラスの単体テストを作成するときは、クラスの機能がパブリックインターフェイスのメソッドに直接実装されているかどうか、または一連のプライベートメソッドに実装されているかどうかを必ずしも気にする必要はありません。そうです、プライベートメソッドをテストする必要がありますが、そうするためにテストコードから直接呼び出す必要はありません(プライベートメソッドを直接テストすると、実装がテストに緊密に結合され、リファクタリングが不必要に難しくなります)。

保護されたメソッドは、クラスとその将来の子の間で異なるコントラクトを形成するため、コントラクトが明確に定義されて実行されていることを確認するために、パブリックインターフェイスと同程度にテストする必要があります。


13

番号!インターフェイスのみをテストします。

TDDの大きな利点の1つは、プライベートメソッドの実装をどのように選択したかに関係なく、インターフェイスが確実に機能することです。


11

他の人が上で言ったことを完了すると、保護されたメソッドはある種のインターフェースの一部であると言えます。それは、インターフェースを検討するときに誰もが考えがちな構成ではなく、継承にさらされるメソッドです。

メソッドをプライベートではなく保護としてマークすると、サードパーティのコードで使用されることが予想されるため、継承と構成の両方で開かれているパブリックメソッドで定義された通常のインターフェイスと同様に、何らかのコントラクトを定義してテストする必要があります。 。


9

テストを書く理由は2つあります。

  1. 期待される行動を主張する
  2. 行動の退行を防ぐ

(1)期待される行動の主張:

期待される動作を主張するときは、コードが期待どおりに機能することを確認する必要があります。これは事実上、あらゆる種類のコードを実装するときに開発者が実行するルーチンの手動検証を行う自動化された方法です。

  • 私が書いたものはうまくいきましたか?
  • このループは実際に終了しますか?
  • 思った順番でループしているのでしょうか?
  • これはnull入力に対して機能しますか?

これらは私たち全員が頭の中で答える質問です。通常、私たちは頭の中でコードを実行しようとします。それが機能するように見えることを確認してください。これらの場合、コンピュータに明確な方法でそれらに答えさせることがしばしば役に立ちます。したがって、それを主張する単体テストを作成します。これにより、コードに自信が持てるようになり、欠陥を早期に発見でき、実際にコードを実装することもできます。

必要と思われる場所でこれを行うことをお勧めします。理解するのが少し難しい、または重要なコード。些細なコードでもその恩恵を受けることができます。それはすべてあなた自身の自信についてです。それを行う頻度と進む距離は、あなた自身の満足度に依存します。自信を持って「はい」と答えられるようになったら停止します。これで問題なく動作しますか?

この種のテストでは、可視性やインターフェースなどは気にせず、コードが機能することだけを気にします。そうです、質問に「はい」と答えるためにテストする必要があると感じた場合は、プライベートメソッドと保護されたメソッドをテストします。

(2)行動の退行の防止に関する見解:

動作するコードを入手したら、このコードを将来の損傷から保護するためのメカニズムを導入する必要があります。誰もあなたのソースと設定に二度と触れないのであれば、これは必要ありませんが、ほとんどの場合、あなたや他の人があなたのソフトウェアのソースと設定に触れます。この内部のいじりは、作業コードを壊す可能性が非常に高いです。

この損傷から保護する方法として、ほとんどの言語にメカニズムがすでに存在します。可視性機能は1つのメカニズムです。プライベートメソッドは分離され、隠されています。カプセル化は、他のコンパートメントを変更しても他のコンパートメントに影響を与えないように、物事をコンパートメント化するもう1つのメカニズムです。

このための一般的なメカニズムは、境界へのコーディングと呼ばれます。コードの一部の間に境界を作成することにより、境界の内側のすべてを境界の外側のものから保護します。境界は相互作用のポイントになり、物事が相互作用する契約になります。

これは、境界のインターフェイスを壊したり、期待される動作を壊したりすることによって境界を変更すると、それに依存する他の境界が損傷し、場合によっては壊れることを意味します。そのため、これらの境界を対象とし、セマンティックと動作が変化しないことを表明する単体テストを用意することをお勧めします。

これはあなたの典型的なユニットテストであり、TDDまたはBDDについて言及するときにほとんどの人が話します。重要なのは、境界を強化し、変化から保護することです。プライベートメソッドは境界ではないため、このためにプライベートメソッドをテストする必要はありません。保護されたメソッドは制限された境界であり、私はそれらを保護します。それらは世界にさらされていませんが、それでも他のコンパートメントまたは「ユニット」にさらされています。

これをどうする?

これまで見てきたように、パブリックメソッドとプロテクトメソッドをユニットテストするのには十分な理由があります。インターフェイスが変更されないと主張するからです。また、実装が機能することを主張するために、プライベートメソッドをテストする正当な理由もあります。では、それらすべてをユニットテストする必要がありますか?

はいといいえ。

まず第一に、可視性に関係なく、コードが機能することを確信できるように、ほとんどの場合に機能するという明確な証拠が必要だと思われるすべてのメソッドをテストします。次に、それらのテストを無効にします。彼らはそこで仕事をしました。

最後に:境界のテストを作成します。システムの他のユニットで使用される各ポイントのユニットテストを行います。このテストがセマンティックコントラクト、メソッド名、引数の数などを表明していることを確認してください。また、テストがユニットの使用可能な動作を表明していることを確認してください。テストでは、ユニットの使用方法と、ユニットで何ができるかを示す必要があります。これらのテストを有効にして、すべてのコードプッシュで実行されるようにします。

注:最初のテストセットを無効にした理由は、リファクタリング作業を実行できるようにするためです。アクティブなテストはコード結合です。それはそれがテストしているコードの将来の変更を防ぎます。これは、インターフェースと相互作用コントラクトにのみ必要です。


1
プライベートメソッドを個別に明示的にテストしない場合、それらはテストの対象外であり、それらが機能することを信頼できないように聞こえます。私はこれが単に間違っていると主張します。パブリックメソッドを介してテストできないプライベートメソッド(またはその中のコードパス)はデッドコードであるため、削除する必要があります。TDDの要点は、テストに合格するために存在しない0 LoCを書き込むため、パブリックメソッドをテストするだけで完全なカバレッジを取得することです。分離されたプライベートメソッドをテストすることは、リファクタリングを困難にするためだけに役立ちます。これは、TDDの目標(の1つ)とは正反対です。
sara

@kaiプライベートメソッドの自動テストを行うべきではないと明示的に述べていますが、実装を支援するためにテストを分離することが重要な場合があります。これらのテストは、テストスイートの一部であってはなりません。または、リファクタリングというまさにその理由で無効にする必要があります。プライベートメソッドを実装するためのプログラムによるテストをいつ行うかを決めるのは、あなた自身の信頼水準次第です。たぶんあなたは私の答えの終わりまで読んでいませんでしたか?
Didier A.

あなたは、「私たちの実装が機能することを主張するために、プライベートメソッドをテストする正当な理由もある」と主張します。投稿にはこれの根拠がありません。プライベートメソッドのテストでは、パブリックメソッドのテストでもわかりませんが、実際の実装については何もわかりません。プライベートメソッドは機能するか、機能しません。それが機能しない場合は、1つ以上のパブリックメソッドのテストが失敗するか、コードが機能していないか、テストされていません。
sara 2015

@kaiあなたが言及する:「またはそれは死んでいるおよび/またはテストされていないコードです」。テストされていないコードは私が話しているものです。プライベートメソッドは、エッジケースが実行されていない多くのバグをパブリックメソッドから隠すことができます。1つのエラーによるオフを想像してみてください。時々、パブリックメソッドの不変条件がそれを作るので、このケースは決して起こりません。そのような場合、私はプライベートメソッドがまだバグがあり、実装に欠陥があると考えますが、その統合により、バグが発見されてキャッチされるのを防ぎます。この場合、メソッドにバグがないことを確認できるように、エッジケースを試すためにいくつかのテストが必要になる場合があります。
ディディエA.

@kaiしかし、私が話しているこれらのテストはあなたのテストスイートではないことを理解してください。これはTDDではありません。私が言っているのは、いくつかのプライベートメソッドは、それらに対していくつかのテストをすばやく実行できれば、実装が簡単になるということです。REPLのある言語では、それほど必要ありません。そして、頭の中でメソッドをステップスルーしてみることができますが、プライベートメソッドを実装するのが難しい場合にのみ、代わりにコンピューターで実行するテストをお勧めします。後でテストを削除するか、テストを無効にしておくか、CIビルドで実行されない独自の特別な場所に置くことをお勧めします。
ディディエA.

4

いいえ、プライベートメソッドをテストするべきではありません(リフレクションのような恐ろしいものを使用せずにとにかくどうしますか)。保護されたメソッドを使用すると、C#で物事を内部で保護できることは少しわかりにくくなります。これを実行して、テンプレートパターンメソッドを介してすべての機能を実装する派生クラスをテストすることは問題ないと思います。

ただし、一般に、パブリックメソッドの処理が多すぎると思われる場合は、クラスをよりアトミックなクラスにリファクタリングしてから、それらのクラスをテストするときが来ました。


2

私もプライベートメソッドをテストしないという@kwbeamの答えに同意します。ただし、強調したい重要な点は、保護されたメソッドはクラスのエクスポートされたAPIの一部であるため、テストする必要があります。

保護されたメソッドは公開されていない可能性がありますが、サブクラスがそれらを使用/オーバーライドする方法を確実に提供しています。クラス外の何かがそれらにアクセスできるため、それらの保護されたメンバーが期待どおりに動作することを確認する必要があります。したがって、プライベートメソッドをテストするのではなく、パブリックメソッドと保護されたメソッドをテストしてください。

重要なロジックを含むプライベートメソッドがあると思われる場合は、それを別のオブジェクトに抽出し、分離して、その動作をテストする方法を提供しようと思います。

それが役に立てば幸い!


2

高いコードカバレッジを目指している場合(そうすることをお勧めします)、プライベートまたは保護されているかどうかに関係なく、すべてのメソッドをテストする必要があります。

保護は一種の異なる議論のポイントですが、要約すると、それはまったく存在すべきではありません。デプロイされたコードのカプセル化を破るか、ユニットテストのためだけにそのクラスから継承するように強制します。継承する必要がない場合もあります。

クライアントにメソッドを非表示にする(プライベートにする)だけでは、監査されない特権を持つことはできません。したがって、前述のように、パブリックメソッドでテストできます。


1

私は他のみんなに同意します:あなたの質問への答えは「いいえ」です。

確かに、あなたはあなたのアプローチとあなたの考え、特にコードカバレッジについて完全に正しいです。

また、質問(および回答「いいえ」)は、クラスに導入する可能性のあるパブリックメソッドにも適用されることを付け加えておきます。

  • テストに合格しなかったためにメソッド(パブリック/プロテクトまたはプライベート)を追加すると、TDDの目標はほぼ達成されました。
  • TDDに違反することを決定したためにメソッド(パブリック/保護またはプライベート)を追加した場合、コードカバレッジはこれらをキャッチし、プロセスを改善できるはずです。

また、C ++の場合(そしてC ++についてのみ考える必要があります)、プライベートメソッドのみを使用してインターフェイスを実装し、クラスが実装するインターフェイスを介してのみ使用する必要があることを示します。テストから実装に追加された新しいメソッドを誤って呼び出すのを防ぎます


0

優れた設計とは、アプリケーションを複数のテスト可能なユニットに分割することを意味します。これを行った後、一部のユニットはパブリックAPIに公開されますが、一部のユニットは公開されない場合があります。また、公開されたユニットとこれらの「内部」ユニット間の相互作用ポイントも、pubicAPIの一部ではありません。

識別可能なユニットができたら、パブリックAPIを介して公開されているかどうかに関係なく、ユニットテストの恩恵を受けると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.