0

単体テストを学習しようとしていますが、それによる設計上の問題があります。クラス A がクラス B に依存しているとします。A を単体テストするために B のスタブを作成する場合、ほとんどの分離フレームワークでは、B がインターフェイスである必要があるか、A で使用されるすべてのメソッドが仮想である必要があります。B は、単体テストのために本質的に非仮想メソッドを持つ具象クラスにすることはできません。

これにより、製品コードの設計に大きな制限が課せられます。すべての依存関係に対してインターフェイスを作成する必要がある場合、クラスの数は 2 倍になります。単一責任の原則に従うと、相互に依存する小さなクラスが発生するため、インターフェイスの数が大幅に増加します。また、揮発性の依存関係 (将来変更される可能性があります) のインターフェイスを作成するか、設計で拡張性が必要な場合に使用します。テストのためだけにインターフェースで本番コードを汚染すると、その複雑さが大幅に増加します。すべてのメソッドを仮想化することも、良い解決策ではないようです。これにより、継承者は、これらのメソッドがオーバーライドされていなくてもオーバーライドされても問題ないという印象を与えられます。実際には、これは単体テストの副作用にすぎません。

これは、テスト可能なオブジェクト指向設計では具体的な依存関係が許可されないということですか、それとも具体的な依存関係を偽造してはならないということですか? 「ユニットテストを正しく行うには、すべての依存関係を偽造(スタブまたはモック)する必要があります」これまでに学んだことなので、後者はそうではないと思います。JustMock と Isolator 以外の分離フレームワークでは、仮想メソッドなしで具体的な依存関係を偽造することはできず、JustMock と Isolator の力が悪い設計につながると主張する人もいます。任意のクラスをモックできる機能は非常に強力であり、自分が何をしているのかを理解していれば、プロダクション コードの設計をクリーンに保つことができると思います。

4

1 に答える 1

0

この質問も同じことを尋ねており、解決策がないように思われることに後で気付きました。インターフェイスを作成するか、すべてのメソッドを仮想化するかの選択は、静的型付け言語である C# の制限です。Ruby などのダックタイプの言語はこれを強制せず、元のクラスを変更せずに偽のオブジェクトを簡単に作成できます。Ruby では、適切なメソッドを作成するだけで、元の依存関係の代わりに使用できます。

編集:

Roy Osherove による The Art of Unit Testing という本を読み終えたところ、次の段落が関連していることがわかりました。

通常、テスト可能な設計は、C# や VB.NET などの静的言語でのみ問題となります。このような言語では、テストの容易性は、置き換え可能な積極的な設計の選択に依存します。デフォルトではるかにテストしやすい動的な言語では、テスト容易性のための設計はそれほど重要ではありません。そのような言語では、プロジェクトの設計に関係なく、ほとんどのものを簡単に置き換えることができます。これにより、そのような言語のコミュニティは、コードのテスト可能性の欠如はコードの設計が悪いことを意味し、より深いレベルで優れた設計が達成すべきものに集中できるようになるという、ストローマンの議論から解放されます。

テスト可能な設計には、仮想メソッド、封印されていないクラス、インターフェイス、および関心の明確な分離があります。静的クラスとメソッドが少なくなり、ロジック クラスのインスタンスが多くなります。実際、テスト可能な設計は SOLID 設計原則と相関していますが、必ずしも優れた設計であるとは限りません。おそらく、最終目標はテスト容易性ではなく、優れた設計のみであるべき時です。

これは基本的に、静的言語の制限のためにデザインをテスト可能にすることは、本質的に「良いデザイン」にはならないことを意味します。私にとって、優れた設計とは、今日の要件に必要なものを実現し、将来についてあまり考えないものです。もちろん、すべての依存関係を抽象化することは、将来の保守性に役立ちますが、API が非常に複雑になります。依存関係が変更される可能性がある場合、または多くの具体的なクラスがそのインターフェイスを実装している場合は、依存関係をインターフェイスにしたいと考えています。テスト容易性が必要だからではありません。テスト可能性が必要なため、これを行うと、「悪い設計」につながります。

于 2015-10-31T07:38:11.217 に答える