web-dev-qa-db-ja.com

単体テストには依存性注入が不可欠ですか?

単体テストに 依存性注入 (DI)の使用は不可欠ですか?

テストできるようにコードを分離する別の方法は考えられません。また、これまでに見た例はすべてこのパターンを使用しています。それが唯一の実行可能なオプションなのか、それとも他の選択肢があるのですか?

57
Tom Squires

DIを使用すると、単体テストがはるかに簡単になります。ただし、DIがなくても単体テストを作成できます。 DIが普及する前に、多くの単体テストが既に作成されています。 (もちろん、これらの使用されたテクニックのいくつかは、ファンシーな名前を持っていることを知らずにDIと同一または非常に類似しています:-)

私自身、例えばDIについて学ぶ前に、インターフェースとファクトリーをたくさん使っています。実際のファクトリクラス名は、構成ファイルから読み取られたか、引数としてSUTに渡された可能性があります。

別のアプローチは、シングルトン(または一般にグローバルにアクセス可能なデータ)を使用することです。はい、多くの人(自分を含む)からは推奨されていません。それでも、特定の状況、特にシングルトンにテストケース固有ではないが、本番環境とテスト環境で異なる静的構成データが含まれている場合、それは可能です。もちろん、既知の問題があるので、使えるならDIの方が優れています。しかし、多くの場合(たとえば、レガシーシステムでは)できません。

その話 レガシーコードで効果的に動作する は、テストでレガシーコードをカバーするための多くのトリックを説明します。これらの多くはナイスではなく、長期的な解決策ではありません。しかし、これらを使用すると、他の方法ではテスト不可能なシステムに対して最初の価値のあるユニットテストを作成できます。これにより、リファクタリングを開始し、最終的に(とりわけ)DIを導入できます。

43
Péter Török

デカップリングは単体テストに不可欠です。 DIはデカップリングを実現するための優れた方法です。

55
nlawalker

使用しているテクノロジーによっては、DIを使用せずに依存関係を分離できます。たとえば、.NETの世界では、 Moles を使用すると、DIパターンなしで依存関係を分離できます。

とはいえ、これらの分離フレームワークは、外部依存関係(ファイルシステム、データベースなど)を使用するコード内の状況向けに作成され、意図されていると思います。つまり、これを実行できるということは、彼または彼女がそうするべきだという意味ではありません。

依存性注入により、単体テストが可能になりますが、オブジェクトのコードを変更せずにオブジェクトの動作を変更することもできます(オープン/クローズの原則)。つまり、テスト可能なコードだけでなく、結果として生じる柔軟なコードでもあります。私は一般に、保守可能/柔軟なコードとテスト可能なコードの間に強い相関があることを発見しました。

10
Erik Dietrich

いいえ、DIは単体テストに必須ではありませんが、非常に役立ちます。

factories or locatorsを使用して、DIを使用する場合と同じようにテストできます(あまりエレガントではなく、さらにセットアップが必要になります)。

また、依存関係ではなく関数に多くの呼び出しが委任されるレガシーシステムでは、モックオブジェクトが重要になります。 (モックオブジェクトも適切な設定で広範囲に利用できます)

テストがほとんど不可能であるセットアップがある場合があります。しかし、これは依存性注入が使用されているかどうかには基づいていません。

4
smp7d

No、単体テストでは依存関係の注入は必須ではありません。

依存性注入は、サブ処理を実行するために依存クラスインスタンスを必要とするクラスがある場合に役立ちます。 DIの代わりに、ビジネスメソッドのロジックを、データの結合部分(単体テスト不可)と単体テスト可能な計算部分に分けることができます。

例(DIを使用)この実装は、従業員、アカウントなどに依存します。

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     if (amount > 100 && employee.isStudent())
        return false;
     if (to.getOwner().getFamiliyName() == employee.getFamilyName() && ...
        return false; // cannot transfer money to himself;
     ...
 }

データ収集と計算の分離後:

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     return hasPermissionToTransferMoney(employee.isStudent(), employee.getFamilyName(), to.getOwner().getFamilyName(), ...);
 }

 // the actual permission calculation
 static bool hasPermissionToTransferMoney(boolean isStudent, string employeeFamilyName, string receiverFamilyName, ...)
     if (amount > 100 && isStudent)
        return false;
     if (receiverFamilyName == employeeFamiliyName && ...
        return false; // cannot transfer money to himself
     ...
 }

計算部分は、依存性注入なしで簡単にテストできます。

3
k3b
  • 依存性注入は単体テストに必須ではありません

  • 一方、ある実装を別の実装と交換する場合は、制御の反転が不可欠です。

1
CodeART

はい、DIを使用して分離する方法の代替手段があります。

1つの代替方法は、ファクトリーまたはServiceLocatorを使用して、実際のオブジェクトではなくモックオブジェクトを返すようにテストから構成できます。

もう1つは、適切な分離フレームワークまたはモックツールを使用することです。このようなツールは、ほとんどすべての最新のプログラミング言語(少なくとも、Java、C#、Ruby、Python)に存在します。テストされたクラスがその依存関係を直接インスタンス化する場合でも、テストされているクラスを他のクラス/タイプの実装から分離できます。

0
Rogério