web-dev-qa-db-ja.com

Private / Protectedメソッドは単体テスト下にあるべきですか?

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

さて、私の質問は、インターフェースによって公開されているメソッド/プロパティをサポートするためにクラスで記述する必要があるプライベートおよび保護されたメソッドについてです。

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

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

私の考え:

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

  • 私はインターフェイスを使用しているため、定義されたコントラクトがインターフェイスを実装するさまざまなクラスによって適切に実装されていることを検証する単体テストを作成しています。インターフェースによって定義されたメソッド/プロパティ。

  • コードカバレッジで保護/プライベートメソッドがヒットしていることが示されない場合、適切なユニットテストがないか、使用されていないコードを削除する必要があります。

74
Raj Rao

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

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

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

100
user122376

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

最も重要なルールは次のとおりです。コードトランプを使用すると、public/protected/privateに関する理論上のルールが決まります。

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

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

子供たちにテストを与えた心理学者についての素晴らしい物語があります。彼はそれぞれの端にロープを取り付けた2枚の木の板を子供たちに渡し、足を床に触れないで部屋をできるだけ早く横断するように頼みました。すべての子供たちは、小さなスキーのようにボードを使用し、各ボードに片足ずつ、ロープでそれらを保持し、床を横切って滑りました。それから彼は彼らに同じタスクを与えたが、1つのボードだけを使用した。彼らは単一のボードの両端に1フィートずつ、床を旋回/「歩き」ました。

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

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

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

しかし、結局のところ、テストの不足によって引き起こされたバグの数を考えてください。次に、「過度に見える」メソッドによって引き起こされたバグの数と比較します。その答えがあなたの決定を後押しするはずです。

38
Charles Roth

あなたが書いた:

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

これを [〜#〜] bdd [〜#〜] 言語で言い換えてください:

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

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

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

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

お役に立てれば!

34
Lunivore

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

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

14
forsvarir

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

TDDの大きな利点の1つは、プライベートメソッドの実装方法に関係なく、インターフェイスが機能することを保証することです。

12
S.Lott

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

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

8
FGM

テストを作成する理由は2つあります。

  1. 期待される動作を表明する
  2. 行動の回帰を防ぐ

(1)予想される動作のアサート:

予想される動作をアサートしている場合、コードが想定どおりに機能することを確認する必要があります。これは事実上、あらゆる種類のコードを実装するときに開発者が実行するルーチンの手動検証を行う自動化された方法です。

  • 書いただけでうまくいきましたか?
  • このループは実際に終了しますか?
  • それは私が思う順序でループしていますか?
  • これはヌル入力に対して機能しますか?

これらはすべて私たちの頭の中で答える質問であり、通常、私たちも頭の中でコードを実行しようとし、それが機能するように見えることを確認します。これらの場合、コンピュータに決定的な方法で回答させることがしばしば有用です。そのため、それをアサートする単体テストを作成します。これにより、コードに自信が持て、欠陥を早期に発見するのに役立ち、コードを実際に実装するのに役立ちます。

必要だと感じる場所ならどこでもこれを行うのは良い考えです。理解するのが少し難しい、または些細でないコード。些細なコードでさえも恩恵を受けることができます。それはすべてあなた自身の自信についてです。どのくらいの頻度で、どれだけ遠くまで行けるかは、あなた自身の満足度に依存します。自信を持って「はい」と答えられるようになったら停止します。

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

(2)動作の退行防止:

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

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

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

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

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

これをどうする?

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

はいといいえ。

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

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

注:テストの最初のセットを無効にした理由は、リファクタリング作業を許可するためです。アクティブなテストはコード結合です。テスト中のコードが将来変更されるのを防ぎます。これは、インターフェースと対話コントラクトにのみ必要です。

7
Didier A.

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

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

4
briantyler

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

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

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

それが役に立てば幸い!

2
codematix

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

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

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

2
Teoman shipahi

私は他の皆に同意します:あなたの質問に対する答えは「いいえ」です。

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

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

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

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

1
quamrana

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

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

0
h22