web-dev-qa-db-ja.com

単体テストでカプセル化が壊れることはありますか?

「プライベートメソッドをテストしたい場合は、それを別のクラスに入れて公開した方がいい」とよく耳にします。

ときどきそうであり、クラス内に非表示の概念がある場合もあれば、同じ属性(または最悪の場合、1つのクラスのすべての属性が他のクラスのメソッドの引数になる)を持つクラスになってしまい、実際には実装の詳細である機能。

特にTDDでは、以前にテストされたクラスのパブリックメソッドを持つクラスrefactorの場合、そのクラスはインターフェイスの一部になりますが、テストはありません(リファクタリングされているため、実装の詳細)。

今、私は明白なより良い答えを見つけることができないかもしれませんが、私の答えが「正しい」である場合、それは時々ユニットテストを書くことがカプセル化を壊し、同じ責任を異なるクラスに分けることを意味します。

簡単な例は、実際のコードの何にもゲッターが実際には必要ない場合のセッターメソッドのテストです。

私が書いたかもしれない特定のケースに対する簡単な答えを提供しないでください。むしろ、一般的なケースと理論的アプローチの詳細を説明してください。そして、これは言語固有ではありません。

前もって感謝します。

編集:マシューフリンの回答は非常に洞察に満ちていましたが、質問には完全には回答しませんでした。プライベートメソッドは実際には他の懸念事項であり責任があるため(または少なくともそれが彼の回答から理解できたため)、プライベートメソッドをテストしたり抽出したりしないという公正な意見を述べましたが、プライベートテストをプライベートで行う状況があると思いますメソッドは便利です。私の主な例は、1つの責任を持つクラスがあるが、それが与える(取る)出力(または入力)が複雑である場合です。たとえば、ハッシュ関数です。ハッシュ関数を分解し、まとまりとカプセル化を維持する良い方法はありません。ただし、ハッシュ関数をテストすると、ハッシュを手動で計算し(コード計算を使用してコード計算をテストすることはできません!)、ハッシュが変化する複数のケースをテストする必要があるため、非常に困難な場合があります。そのように(そしてこれはそれ自体のトピックの価値がある質問かもしれません)私はプライベートメソッドテストがそれを処理する最良の方法だと思います。さて、別の質問をするべきか、ここで質問するべきかはわかりませんが、そのような複雑な出力(入力)をテストするためのより良い方法はありますか?

OBS:そのトピックについて別の質問をする必要があると思われる場合は、コメントを残してください。 :)

EDIT2:私の質問に完全には答えられなかったものの、自分の行動方針を考え、決定させるため、正しい答えを受け入れました。しかし、私と同じ問題(一緒に変化するが、それでも自分でテストするのが難しい1つのまとまりのあるクラス)に直面している人のために、私が何をしたのか、そしてその理由を説明します。そのクラスの出力は、コンピューターで正しくテストするには難しすぎると判断したため、テストしませんでした。フレームワークを使用してプライベートメソッドをテストすることもできましたが(これは最良のアイデアだと思います)、その時点まで到達したくありませんでした。それが一体でSRPを尊重しているのが何であるか疑問に思っているのに、コンピューターでテストするのが難しすぎる場合は、いくつかの例を挙げます:ハイトマップ生成、ハッシュ関数、手続き型音楽生成(一部のユニットをテストできますが、最高レベルの単位は単に主観的すぎる)。

27
user1288851

私が若い頃にこの質問を提起したとき、プライベートメソッドの単体テストを書く必要は本当にないと言われました。驚くべきことに、これは事実であることが判明しました。

ユニットテストに行動主導型のアプローチを採用する場合、これは完全に理にかなっています。パブリックメソッドは、何かをするように求められているものです。内部のプライベートメソッドを呼び出すという事実は、外部の動作とは直交しています。他のメソッドはプライベート/カプセル化されているため、実際にはテストされているユニットの一部です。

クラス内の複数のメソッドがすべて単一のプライベートメソッドを呼び出す場合、その1つのメソッドが機能することをテストするべきではないかと尋ねる場合があります。ここでの答えは「はい」ですが、privateメソッドの単体テストでは明らかになりませんが、それを呼び出すpublicメソッドの単体テストでは明らかになりません。パブリックメソッドが機能し、プライベートメソッドを呼び出す場合、プライベートメソッドも機能する必要があります。

39
Matthew Flynn

プライベートメソッドをテストするという衝動を感じるときは、単に間違っているのです。 mightプライベートメソッドの複雑なアルゴリズムを別の「よりパブリックな」クラスに分解して、このクラスを元のプライベートメソッドで使用してもかまいません。しかし、これはまだカプセル化を壊さないということです。

何かをプライベートにしても、それはクラスのAPIの一部ではなく、外界には存在しません。本当に重要であり、あなたの意見ではテストする必要があるが、APIを介して適切に検出できない何かがそこで発生した場合、APIデザインの何かが壊れています。何かが重要か、それが実装の詳細であるかを決定する必要があります。両方にすることはできません。

9
Landei

「プライベートはプライベートです」という答えには同意しませんが、ほとんどの場合、これは事実ですが、常にそうとは限りません。

コード製品が認定された安全が重要な製品(航空宇宙、医療、自動車など)の一部である場合、テストカバレッジ要件に遭遇し、 変更された条件/意思決定カバレッジ

private is privateはボックスにチェックマークを付けないため、クラスの内部をテストしていないことを認証機関に伝えます。

クラスのパブリックインターフェースのテストのみが ブラックボックス テストです...そして、結果に問題がなければ、(ほとんどの場合)それで十分です。しかし、時々ホワイトボックス テストを実行し、内部のナットとボルトを検証する必要があります。

時々を強調しますが、(ほとんどの「ルール」と同様に)「決してプライベートをテストしない」とは言いません。

7
Andrew

まず第一に、コードではなく、クラスの振る舞いがテストされます。コードはクラスを期待どおりに機能させるための単なる方法ですが、コードはまったく必要ありません:)実際には、コードを記述せずにクラスを機能させることをお勧めします(OFF TOPIC)。

パブリックメソッドはプライベートメソッドを呼び出すため、パブリックメソッドのテストで常に十分です。 TDDを使用する場合は、実装の前にテストを作成する必要があるため、プライベートメソッドをテストすることはできません。

健康に重要なソフトウェアなどについては、プライベートメソッドをテストして100%のパスカバレッジを達成しても、テストされたソフトウェアが100%のケースで正しく機能することは保証されません。すべての可能なオブジェクト状態ですべての可能な入力データをテストした後でないと、確信が持てません。これをパブリックメソッドに対してのみ行うと、プライベートメソッドのすべてのパスがカバーされます。

4

保護されたメンバーまたはプライベートメンバーへのアクセス権をテストすることには、時々価値があります。

初期化関数があるとします。特定の内部変数が設定されていることをテストしたいとします。これを行う最も簡単な方法は、テストフレームワークがこれらの変数にアクセスできるようにすることです。通常、テストクラスはクラスから派生するか、テスト中にprotected-> publicに変換するトリックがいくつかあります。

Public()関数を追加して、すべての保護された変数の値を返すこともできますが、それではカプセル化がかなり損なわれます。

0
Martin Beckett