はい、それは良い出発点ですが、依存関係を注入することよりも、インターフェースを持つことの方が優先度が低くなります。すべてのレガシー クラスがインターフェイスを取得しても、内部的に隠されている場合でも、それらはすべて相互依存しているため、クラスのテストは容易ではありません。たとえば、次のような 2 つのクラスがあるとします。
Public Class LegacyDataAccess
Public Function GetAllSales() As List(Of SaleDto)
' Do work with takes a long time to run against real DB
End Function
End Class
Public Class LegacyBusiness
Public Function GetTotalSales() As Integer
Dim dataAccess As New LegacyDataAccess()
Dim sales As List(Of SaleDto) = dataAccess.GetAllSales()
' Calculate total sales
End Function
End Class
私はあなたがすでに言っていることを知っています... 「レガシーコードが少なくともうまく階層化されていればいいのに」ですが、テストが難しいレガシーコードの例としてそれを使用しましょう。テストが難しい理由は、コードがデータベースに到達し、データベースに対して時間のかかるクエリを実行し、その結果を計算するためです。そのため、現在の状態でテストするには、最初に一連のテスト データをデータベースに書き出してから、コードを実行して、挿入されたデータに基づいて正しい結果が返されるかどうかを確認する必要があります。そのようなテストを書かなければならないことには問題があります:
- テストをセットアップするためのコードを書くのは面倒です
- 外部データベースが適切に機能し、正しいサポート データがすべて含まれているかどうかに依存するため、テストは脆弱になります。
- テストの実行に時間がかかりすぎます
お気づきのとおり、インターフェイスは単体テストにとって非常に重要です。したがって、あなたが推奨するように、インターフェイスを追加して、テストが簡単になるかどうかを確認しましょう。
Public Interface ILegacyDataAccess
Function GetAllSales() As List(Of SaleDto)
End Interface
Public Interface ILegacyBusiness
Function GetTotalSales() As Integer
End Interface
Public Class LegacyDataAccess
Implements ILegacyDataAccess
Public Function GetAllSales() As List(Of SaleDto) _
Implements ILegacyDataAccess.GetAllSales
' Do work with takes a long time to run against real DB
End Function
End Class
Public Class LegacyBusiness
Implements ILegacyBusiness
Public Function GetTotalSales() As Integer _
Implements ILegacyBusiness.GetTotalSales
Dim dataAccess As New LegacyDataAccess()
Dim sales As List(Of SaleDto) = dataAccess.GetAllSales()
' Calculate total sales
End Function
End Class
これでインターフェースができましたが、実際には、それによってどのようにテストが簡単になるのでしょうか? これで、同じインターフェイスを実装するモック データ アクセス オブジェクトを簡単に作成できますが、それは実際には中心的な問題ではありません。問題は、実際のデータ アクセス オブジェクトではなく、そのモック データ アクセス オブジェクトをビジネス オブジェクトに使用させるにはどうすればよいかということです。そのためには、依存性注入を導入して、リファクタリングを次のレベルに引き上げる必要があります。本当の犯人はNew
、ビジネス クラスの次の行のキーワードです。
Dim dataAccess As New LegacyDataAccess()
ビジネス クラスは明らかにデータ アクセス クラスに依存していますが、現在はその事実を隠しています。依存関係について嘘をついています。簡単です。このメソッドを呼び出すだけで、結果が返されます。必要なのはそれだけです。実際には、それよりもはるかに多くの時間がかかります。さて、依存関係について嘘をつくのをやめて、恥ずかしがらずに次のように述べたとしましょう。
Public Class LegacyBusiness
Implements ILegacyBusiness
Public Sub New(dataAccess As ILegacyDataAccess)
_dataAccess = dataAccess
End Sub
Private _dataAccess As ILegacyDataAccess
Public Function GetTotalSales() As Integer _
Implements ILegacyBusiness.GetTotalSales
Dim sales As List(Of SaleDto) = _dataAccess.GetAllSales()
' Calculate total sales
End Function
End Class
ご覧のとおり、このクラスははるかに簡単にテストできます。モック データ アクセス オブジェクトを簡単に作成できるだけでなく、モック データ アクセス オブジェクトをビジネス オブジェクトに簡単に挿入できるようになりました。これで、返してほしいデータを正確にすばやく簡単に返すモックを作成し、ビジネス クラスが正しい計算を返すかどうかを確認できます (データベースは関係ありません)。
残念ながら、既存のクラスにインターフェースを追加するのは簡単ですが、依存性注入を使用するようにそれらをリファクタリングするには、通常、さらに多くの作業が必要です。最初に取り組むのに最も適したクラスを計画する必要があります。コードのリファクタリング中に既存のコードを壊さないように、コードが以前と同じように機能する中間の古い学校のラッパーを作成する必要がある場合があります。すぐに簡単にできることではありませんが、辛抱強く長期にわたって乗り切れば、それは可能であり、喜んでいただけるでしょう。