インターフェイスを使用する場合(ユニットテスト、IoC?)


17

私はここで男子学生の間違いを犯したのではないかと疑い、明確化を求めています。私のソリューションの多くのクラス(C#)-あえて過半数を言う-私は対応するインターフェースを書くことになりました。たとえば、「ICalculator」インターフェースと、それを実装する「Calculator」クラス。ただし、その計算機を別の実装に置き換えることは決してないでしょう。また、これらのクラスのほとんどは、依存関係と同じプロジェクトに存在します。実際に必要なのはinternal、必要なだけですが、publicそれぞれのインターフェイスを実装する副作用として存在することになりました。

すべてのインターフェイスを作成するこのプラクティスは、いくつかの偽りに起因すると思います。

1)元々、ユニットテストモックを作成するにはインターフェイスが必要だと思っていました(私はMoqを使用しています)が、その後、そのメンバーがvirtualであり、パラメーターのないコンストラクターがある場合、クラスをモックできることがわかりました(私が間違っている)。

2)IoCフレームワーク(Castle Windsor)にクラスを登録するにはインターフェースが必要だと当初考えていました。例えば

Container.Register(Component.For<ICalculator>().ImplementedBy<Calculator>()...

実際、私はそれ自体に対して具体的な型を登録することができました:

Container.Register(Component.For<Calculator>().ImplementedBy<Calculator>()...

3)依存性注入のコンストラクタパラメーターなどのインターフェイスを使用すると、「疎結合」になります。

それで、私はインターフェースに夢中になりましたか?!私は、あなたが「通常」インターフェースを使用するシナリオを知っています。例えば、パブリックAPIを公開したり、「プラグイン可能な」機能のようなもののために。私のソリューションには、そのようなユースケースに適合する少数のクラスがありますが、他のすべてのインターフェースは不要であり、削除する必要があるのでしょうか?上記の3)に関して、これを行う場合、「疎結合」に違反しませんか?

編集:-Moqで遊んでいるだけであり、それらをモックできるようにするには、メソッドがパブリックで仮想であり、パブリックのパラメータレスコンストラクターを必要とするようです。それで、私は内部クラスを持つことができないように見えますか?


インターフェイスがパブリックであっても、C#ではインターフェイスを実装するためにクラスをパブリックにする必要はないと確信しています
...-vaughandroid

1
プライベートメンバーをテストすることになっていない。これは、神クラスのアンチパターンの指標として見ることができます。プライベート機能を単体テストする必要があると感じた場合、通常、その機能を独自のクラスにカプセル化することをお勧めします。
スポイケ

@Spoike問題のプロジェクトはUIプロジェクトであり、すべてのクラスを内部にするのが自然に思えますが、それでもユニットテストが必要ですか?私は内部メソッドをテストする必要がないことについて他の場所で読みましたが、その理由を理解したことはありません。
アンドリュースティーブンス

まず、テスト対象のユニットとは何かを自問してください。クラス全体ですか、それともメソッドですか?ユニットを適切にテストするには、そのユニットを公開する必要があります。UIプロジェクトでは、通常、テスト可能なロジックを、すべてパブリックメソッドを持つコントローラー/ビューモデル/サービスに分離します。実際のビュー/ウィジェットはコントローラー/ビューモデル/サービスに接続され、通常の煙テストでテストされます(つまり、アプリを起動してクリックを開始します)。基礎となる機能をテストする必要があるシナリオがあり、ウィジェットで単体テストを行うと脆弱なテストが発生するため、注意して実行する必要があります。
スポイケ

@Spoikeでは、ユニットメソッドにアクセスできるようにするために、VMメソッドを公開していますか?おそらくそれは私が間違っているところです、すべてを内部にしようと懸命にしようとしています。私の手はMoqと結びついているかもしれませんが、モックするためにはクラス(メソッドだけでなく)を公開する必要があるようです(Telastynの答えに関する私のコメントを参照)。
アンドリュースティーブンス

回答:


7

一般に、実装者が1人だけのインターフェースを作成する場合は、2回書くだけで時間を無駄にします。インターフェースだけでは、1つの実装に密接に結合している場合、疎結合を提供しません。コード。

ただし、ほぼすべてのクラスにインターフェイスがある場合は、少しコードが臭いだと思います。それは、彼らがほぼすべて何らかの形で互いに協力し合っていることを意味します。クラスが多すぎる(ヘルパークラスがないため)か、抽象化しすぎているのではないかと思われます(ああ、重力のためのプロバイダーインターフェイスが変更される可能性があるためです!)。


1
返信いただきありがとうございます。前述のように、ほとんどのインターフェイスに複数の実装が含まれることはないと知っていたにもかかわらず、私がやったことをやった理由がいくつかあります。問題の一部は、インターフェイスをモックするのに最適なMoqフレームワークを使用していることですが、クラスをモックするには、クラスをパブリックにする必要があります。
アンドリュースティーブンス

私が単体テストしているクラスのほとんどは内部クラスであり、テスト可能にするためだけに公開したくはありません(議論できるかもしれませんが、これらはすべてこれらのインターフェイスのために書いたものです)。インターフェースを削除したら、おそらく新しいモックフレームワークを見つける必要があるでしょう!
アンドリュースティーブンス

1
@AndrewStephens-世界中のすべてがm笑される必要はありません。クラスがインターフェイスを必要としないほど堅固(または密結合)である場合、単体テストでそのまま使用できるほど堅固です。それらを監視する時間/複雑さは、テストの利点に値しません。
テラスティン

-1。多くの場合、最初にコードを記述するときに必要な実装者の数がわからないためです。インターフェースを最初から使用する場合、追加の実装者を作成するときにクライアントを変更する必要はありません。
ウィルバート

1
@wilbert-確かに、小さなインターフェースはクラスよりも読みやすいですが、どちらかを選ぶのではなく、クラスまたはクラスインターフェースのどちらかがあります。そして、あなたは私の答えを読みすぎているかもしれません。私は、単一の実装のために決してインターフェースを作成しないと言っているわけではありません-ほとんどの相互作用はその柔軟性を必要とするので、実際にはほとんどのはずです。しかし、もう一度質問を読んでください。すべてのインターフェイスを作成することは、単に貨物を栽培することです。IoCは理由ではありません。モックは理由ではありません。要件は、その柔軟性を実装する理由です。
テラスティン

4

1)具象クラスがモック可能であっても、メンバーvirtualを作成し、パラメーターを持たないコンストラクターを提供する必要があります。あなた(または新しいチームメンバー)はvirtual、「それが物事の仕組みだ」という理由だけで、何も考えずに、すべての新しいクラスにの、およびパラメーターなしのコンストラクターを体系的に追加することにすぐに気付くでしょう。

依存関係をモックする必要がある場合、インターフェースはIMOよりも優れたアイデアです。なぜなら、インターフェースは本当に必要なときまで実装を延期でき、依存関係の明確で明確な契約を可能にするからです。

3)それは偽りですか?

インターフェイスは、コラボレーションするオブジェクト間のメッセージングプロトコルを定義するのに最適だと思います。たとえば、ドメインエンティティのように、それらの必要性が議論の余地がある場合があります。ただし、通信する必要がある安定したパートナー(つまり、一時参照ではなく注入された依存関係)がある場合は、通常、インターフェイスの使用を検討する必要があります。


3

IoCのアイデアは、具象型を置き換え可能にすることです。そのため、(現在)ICalculatorを実装する計算機が1つしかない場合でも、2番目の実装は、計算機からの実装の詳細ではなく、インターフェイスのみに依存する可能性があります。

したがって、IoC-Container登録の最初のバージョンは正しいものであり、今後使用する必要があります。

クラスをパブリックまたは内部にすると、実際には関係がなくなり、どちらもmoq-ingになりません。


2

プロジェクト全体のすべてのタイプについて「モック」する必要性を感じているように見えるという事実について、私は本当にもっと心配しています。

テストダブルは、コードを外部の世界(サードパーティのライブラリ、ディスクIO、ネットワークIOなど)または非常に高価な計算から分離するのに役立ちます。分離フレームワークに寛大すぎる(本当に必要ですか?プロジェクトは複雑ですか?)実装に結び付けられたテストを簡単に導き、設計をより厳格にします。いくつかのクラスがメソッドXおよびYをこの順序で呼び出し、パラメーターa、b、cをメソッドZに渡すことを確認する一連のテストを作成すると、本当に値を取得できますか?ロギングまたはサードパーティのライブラリとの相互作用をテストするときに、このようなテストを取得する場合がありますが、これは例外であり、ルールではありません。

分離フレームワークから、何かを公開する必要があり、常にパラメーターレスコンストラクターが必要であると通知されたら、この時点でフレームワークが悪い(より悪い)コードを書くことを強制しているため、回避する必要があります。

インターフェースについてより具体的に言えば、いくつかの重要な場合に役立つと思います。

  • メソッドの呼び出しを異なるタイプにディスパッチする必要がある場合、たとえば複数の実装がある場合(ほとんどの言語のインターフェイスの定義は仮想メソッドのコレクションであるため、これは明らかです)

  • 残りのコードを何らかのクラスから分離できるようにする必要がある場合。これは、アプリケーションのコアロジックからファイルシステムとネットワークアクセス、データベースなどを抽象化する通常の方法です。

  • アプリケーションの境界を保護するために制御の反転が必要な場合。これは、依存関係を管理するための最も強力な方法の1つです。高レベルのモジュールと低レベルのモジュールはお互いを知らないはずです。なぜなら、それらはさまざまな理由で変化し、ドメインモデルのHttpContextやマトリックス乗算ライブラリのPdfレンダリングフレームワークに依存したくないからです。 。境界クラスにインターフェイスを実装すると(たとえ置き換えられなくても)、レイヤー間の結合を緩めることができ、他の多くのタイプを知っているタイプからレイヤーを介して依存関係が浸透するリスクが大幅に減少します。


1

私は、一部のロジックがプライベートである場合、そのロジックの実装は誰にとっても重要ではなく、したがってテストされるべきではないという考えに傾いています。いくつかのロジックがテストするほど複雑な場合、そのロジックはおそらく明確な役割を持っているため、パブリックにする必要があります。たとえば、プライベートヘルパーメソッドでコードを抽出すると、通常は別のパブリッククラス(フォーマッター、パーサー、マッパーなど)に配置できるものであることがわかります。
インターフェイスについては、クラスをスタブまたはモックする必要性に駆られました。リポジトリのようなもの、私は通常、スタブまたはモックできるようにしたい。統合テストのマッパー(通常)それほど多くはありません。

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