3

クラスに依存する他のクラスを単体テストするために、依存性注入を使用してクラスをモックアウトしています。

class Foo : IFoo
{
    // production code
}

class MockFoo : IFoo
{
    // mock so other classes that depend on Foo can be unit tested
}

class Bar
{
    public DoSomething()
    {
        var barValue = 20;
        // use dependency injection to get Foo instance.
        DependencyInjection.Instance.Foo.ExampleMethod(barValue);
    }
}

しかし、私の依存性注入クラスのセットアップは、扱いにくく、迷路のようで複雑になっています。

public class DependencyInjection
{
    public Setup()
    {
        this.Foo = new Foo();
        this.Bar = new Bar("example constructor string");
        this.Bat = new Bat(123,234);
        // for every single class in my application!
    }
}

(わかりやすくするために簡略化していますが、実際の実装は数十のクラスとそのコンストラクターで構成されていることが想像できます)。

他にもいくつかの問題があります。

  • DependencyInjection とその各クラス インスタンスは、アプリケーション全体で渡される巨大なグローバル変数です。
  • すべてのクラスを同時に初期化しています。
  • 単体テスト用に作成するすべてのクラスにインターフェースを提供する必要があります – 単体テストでプログラムの設計を指示できるようにしています (この制約がなければ、私が作成するほとんどのクラスはインターフェースのない具体的な実装になります) )。

これらの問題を解決する方法についてのアドバイスをいただければ幸いです。

4

4 に答える 4

6

DIを使用することは、コードの分離を突然停止する必要があるという意味ではありません。実装を渡すためにグローバル変数を使用するべきではありません。

機能の特定の部分を解決する必要があるクラスを作成するときは、コンストラクターを介してすべての「外部」依存関係を渡す必要があります。

class Bar
{
    private readonly IFoo _foo;
    public Bar(IFoo foo)
    {
        _foo = foo;
    }

    public DoSomething()
    {
        _foo.ExampleMethod(20)
    }
}

DIのベストプラクティスは、アプリケーションの最初(コンポジションルート)でそれを使用して外部実装を取得し、DIなしの場合と同じように実装を渡すことです。

つまり、テストするために注入 する必要はありません。テストメソッドでモックするだけで完了です。DIは、構成可能なアプリの重要なブロックにのみ使用してください(たとえば、具体的なデータレイヤーの選択など)。IFooBar

于 2012-10-28T18:42:58.460 に答える
4

あなたがすることは、Service Locator (アンチ) パターンです。隠れた依存性注入を提供します (クラスに必要な依存性は明確ではありません)。必要なのは、依存関係を明示的かつ簡単にモックできるメソッド呼び出しインジェクションです。

class Bar
{
    public DoSomething(IFoo foo)
    {
        var barValue = 20;
        // use dependency injection to get Foo instance.       
    }
}

依存性注入の別のタイプは、コンストラクター注入 (IFooコンストラクター パラメーターを介して依存性が提供される場合) とプロパティ注入 (パブリック プロパティを介して提供される依存性) です。

これで、作業を完了するために依存関係がBar必要な API が明らかになりました。IFooそして、この依存関係を簡単にモックできます ( Moqを使用したサンプル):

Mock<IFoo> fooMock = new Mock<IFoo>();
// setup mock
Bar bar = new Bar();
bar.DoSomething(fooMock.Object);

IFoo実行時に実装を挿入する方法は? Ninjectなどの依存性注入フレームワークを使用できます。

于 2012-10-28T18:41:06.873 に答える
2

あなたの箇条書きに答えてみましょう:

依存性注入は概念です。そのためのクラスを作成する必要はありません。コンストラクター注入用にクラスを設計することをお勧めします。DI フレームワークを使用するか、独自のフレームワークを実装する場合、通常のパターンは 2 ステップの API を使用することです。依存関係を登録し、アプリケーションの起動時にそれらを解決します (または、コンポジション ルートで Groo によってより適切に説明されます)。

起動時にクラスを初期化する依存性注入の通常のケースです。ただし、オブジェクトの有効期間が異なる場合は、ライフサイクルを定義したり、ファクトリを使用したりできます。

単体テストでは、クラスのインターフェイスを定義する必要はありませんが、依存関係だけを定義する必要があります。これは、このアプローチの最大の利点の 1 つです。インターフェイスのみに依存するようにクラスを作成している間、これらの依存関係のテスト ダブル (モック、スタブなど) を非常に簡単に作成できます。これにも多くのフレームワークがありますが、これらのインターフェースの独自のテスト実装を自由に使用できます。

于 2012-10-28T19:04:47.097 に答える
0

単体テストでは、通常、コンストラクターをオーバーライドして、依存関係のインスタンスを取り込みます。このようにして、ユニットテストでモックオブジェクトを作成して渡すことができます。

private IFoo foo;

public Bar()
{
    // Production code uses the real thing
    this.foo = new Foo();
}

public Bar(IFoo foo)
{
    // Test code uses a passed-in object, likely a mock
    this.foo = foo;
}

public DoSomething()
{
    foo.DoSomething();
}
于 2012-10-28T18:42:34.233 に答える