5

インターフェイスや抽象クラス、つまり具象クラスのみを持たないプロジェクトを継承しました。単体テストを導入したいと考えています。クラスには、ビジネス ロジックとデータ ロジックを含む多くの関数が含まれています。SOLID のすべてのルールを破っています ( http://en.wikipedia.org/wiki/SOLID_%28object-directional_design%29 )。

私は考えました。設計が不十分なクラスごとにインターフェイスを作成し、すべての関数を公開することを考えていました。その後、少なくともクラスをモックできます。

私は単体テストに比較的慣れていません (適切な場所でインターフェイスを使用して非常によく開発されたプロジェクトの経験があります)。単体テストのためだけに、すべての具体的なクラス (すべての関数とサブルーチンを公開する) のインターフェイスを作成するなど、これを行うことは良い考えですか?

私はこれを調査するのにしばらく時間を費やしましたが、答えが見つかりませんでした。

4

6 に答える 6

2

はい、それは良い出発点ですが、依存関係を注入することよりも、インターフェースを持つことの方が優先度が低くなります。すべてのレガシー クラスがインターフェイスを取得しても、内部的に隠されている場合でも、それらはすべて相互依存しているため、クラスのテストは容易ではありません。たとえば、次のような 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

ご覧のとおり、このクラスははるかに簡単にテストできます。モック データ アクセス オブジェクトを簡単に作成できるだけでなく、モック データ アクセス オブジェクトをビジネス オブジェクトに簡単に挿入できるようになりました。これで、返してほしいデータを正確にすばやく簡単に返すモックを作成し、ビジネス クラスが正しい計算を返すかどうかを確認できます (データベースは関係ありません)。

残念ながら、既存のクラスにインターフェースを追加するのは簡単ですが、依存性注入を使用するようにそれらをリファクタリングするには、通常、さらに多くの作業が必要です。最初に取り組むのに最も適したクラスを計画する必要があります。コードのリファクタリング中に既存のコードを壊さないように、コードが以前と同じように機能する中間の古い学校のラッパーを作成する必要がある場合があります。すぐに簡単にできることではありませんが、辛抱強く長期にわたって乗り切れば、それは可能であり、喜んでいただけるでしょう。

于 2013-08-26T19:12:56.130 に答える
0

すべてを事前にテストする必要はなく、テストする必要があるため、インターフェイスとクラスを作成することを好みます。

インターフェース以外にも、いくつかの手法を使用してレガシー コードをテストできます。私がよく使用するのは「Extract And Override」です。これは、他のメソッド内の「テストできない」コードから一部を抽出し、オーバーライド可能にするものです。これらは、テストするクラスを派生させ、「テスト不可能な」メソッドを何らかのセンシング コードでオーバーライドします。

モック フレームワークの使用は、キーワード Overridable をメソッドに追加し、モック フレームワークを使用して戻り値を設定するのと同じくらい簡単です。

「レガシー コードを効果的に使用する」という書籍には、多くのテクニックが掲載されています。

既存のコードに関する 1 つのことは、単体テストよりも統合テストを作成する方が良い場合があるということです。そして、テスト対象の動作を取得したら、単体テストを作成します。

もう 1 つのヒントは、依存関係の少ないモジュール/クラスから始めることです。そうすれば、コードに慣れるまでの負担が少なくなります。

「抽出とオーバーライド」に関する例が必要な場合はお知らせください;)

于 2013-08-26T18:56:16.923 に答える