単体テストに依存性注入(DI)の使用は不可欠ですか?
テストできるようにコードを分離する別の方法は考えられません。また、私が今まで見たすべての例では、このパターンを使用しています。それは唯一の実行可能なオプションであるか、または他の選択肢があるからでしょうか?
単体テストに依存性注入(DI)の使用は不可欠ですか?
テストできるようにコードを分離する別の方法は考えられません。また、私が今まで見たすべての例では、このパターンを使用しています。それは唯一の実行可能なオプションであるか、または他の選択肢があるからでしょうか?
回答:
DIを使用すると、単体テストがはるかに簡単になります。ただし、DIなしで単体テストを作成することはできます。DIが普及する前に、多くの単体テストがすでに作成されています。(もちろん、これらのいくつかはDIと同じか非常によく似たテクニックを使用しましたが、それは派手な名前を持っていることを知らずに:-)
私自身は、DIについて学ぶ前に、例えばインターフェースや工場をたくさん使ってきました。実際のファクトリクラス名は、構成ファイルから読み取られたか、引数としてSUTに渡された可能性があります。
別のアプローチは、シングルトン(または一般的にグローバルにアクセス可能なデータ)を使用することです。はい、それは一般的に多くの人(自分を含む)に推奨されていないことを知っています。それでもそれはありシングルトンは、テストケース特有の問題ではありません静的構成データが含まれていますが、生産およびテスト環境の間で異なっている場合は特に、特定の状況で生存可能。もちろん、既知の問題があるため、使用できる場合はDIの方が優れています。しかし、多くの場合(たとえば、レガシーシステムでは)できません。
そういえば、レガシーコードを効果的に使用するには、レガシーコードをテストでカバーするための多くのトリックについて説明します。これらの多くは優れたものではなく、長期的な解決策を意図したものでもありません。しかし、そうしないとテストできないシステムに対して最初の貴重な単体テストを作成できます。これにより、リファクタリングを開始し、最終的に(とりわけ)DIを導入できます。
使用しているテクノロジに応じて、DIを使用せずに依存関係を分離できます。たとえば、.NETの世界では、Molesを使用すると、DIパターンなしで依存関係を分離できます。
そうは言っても、これらの分離フレームワークは、外部依存関係(ファイルシステム、データベースなど)のあるコードの状況向けに作成され、意図されていると思います。つまり、これができるという事実は、彼または彼女がすべきだという意味ではありません。
依存性注入により、単体テストが可能になりますが、オブジェクトのコードを変更せずにオブジェクトの動作を変更することもできます(オープン/クローズ原則)。そのため、テスト可能なコードだけでなく、柔軟なコードが生成されます。私は一般に、保守可能/柔軟なコードとテスト可能なコードの間に強い相関関係があることを発見しました。
いいえ、依存性注入は単体テストには不可欠ではありません。
依存性注入は、サブ処理を行うために依存クラスインスタンスを必要とするクラスがある場合に役立ちます。DIの代わりに、ビジネスメソッドのロジックをデータ収集部分(単体テスト不可)と単体テスト可能な計算部分に分離できます。
例(DIを使用)この実装は、Employee、Account、...に依存します。
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
...
}
計算部分は、依存性注入なしで簡単にテストできます。