7

シンプルインジェクタは、かなりの用途で成功しています。私たちはすべてのプロダクションクラスにコンストラクタインジェクションを使用していました。シンプルインジェクタを設定してすべてのものとすべてのオブジェクトを取り込むように設定しました。単体テストでのDIコンテナの使用

単体テストを使用して単体テストの依存関係ツリーを管理することはありませんでした。代わりに、私たちは手動ですべてを新しくしてきました。

私はちょうど大きなリファクタリングの作業に数日を費やしました。私のユニットテストでは、これらの手動で構築された依存関係ツリーを修正することがほとんどでした。

これは私には不思議です - ユニットテストで使用する依存関係ツリーを構成するために使用するパターンは誰にもありますか?少なくとも私たちのテストでは、依存関係のツリーはかなりシンプルになる傾向がありますが、その多くは存在します。

誰でもこれらの管理に使用する方法はありますか?

+0

あなたはどんなパターンを探していますか。テスト初期化(コンストラクタ、xunitなど)でコンテナを作成するだけではどうですか?パターンはシンプルです - 構図。 – Artyom

+1

ユニットテストパターンに本当に関心があるなら、[xUnit Test Patterns](https://www.amazon.co.uk/xUnit-Test-Patterns-Refactoring-Code-ebook/dp/B004X1D36K/ref)を読んでください。 = dp_kinw_strp_1)。 – Steven

答えて

12

真の単体テスト(つまり、1つのクラスだけをテストし、すべての依存関係をモックするもの)では、DIフレームワークを使用する意味がありません。これらの試験において:

  • あなたがnewのための反復コードの多くは、あなたが作成したすべてのモックを使ってクラスのインスタンスをINGの持っていることを発見した場合、一つの有用な戦略は、あなたのモックのすべてを作成することで、 (これらはすべてプライベートインスタンスフィールドにすることができます)、各個別テストの「配置」領域は、模擬する必要があるメソッドの適切なSetup()コードを呼び出すだけで済みます。このようにして、1つのテストクラスにつき1つのnew PersonController(...)ステートメントのみで終了します。
  • 多くのドメイン/データオブジェクトを作成する必要がある場合は、テスト用の正当な値で始まるBuilderオブジェクトを作成すると便利です。そのため、偽の値を持つ巨大なコンストラクタをコード全体で呼び出すのではなく、ほとんどの場合、たとえばvar person = new PersonBuilder().Build()などと呼んでいます。おそらく、そのテストで特に気にかけているデータ。 AutoFixtureに興味があるかもしれませんが、私はそれを使用したことがないので、私はそれを保証することはできません。

あなたはシステムのいくつかの部分の間の相互作用をテストする必要があり統合テストを書いているが、あなたはまだ、特定の部分を模擬あなたのサービスのためのビルダークラスを作成することを検討することができるようにする必要がある場合は、そう、あなたは言うことができる、例えばvar personController = new PersonControllerBuilder.WithRealDatabase(connection).WithAuthorization(new AllowAllAuthorizationService()).Build()

システム全体をテストする必要のあるエンドツーエンドの「シナリオ」テストを作成する場合は、実際の製品と同じ設定コードを使用してDIフレームワークを設定することが理にかなっています使用する。設定を少し変更することで、どのユーザがログインしているのかといったようなものをプログラム的に制御することができます。データを作成するために作成した他のBuilderクラスも引き続き使用できます。

var user = new PersonBuilder().Build(); 
using(Login.As(user)) 
{ 
    var controller = Container.Get<PersonController>(); 
    var result = controller.GetCurrentUser(); 
    Assert.AreEqual(result.Username, user.Username) 
} 
+1

私は[Object Mother](http://xunitpatterns.com/Test%20Helper.html)は単体テストの場合のビルダーパターンの名前だと思います。ニースの答えbtw。 +1 – Steven

+0

反対側の回答:https://softwareengineering.stackexchange.com/questions/140992/is-dependency-injection-essential-for-unit-testing – sotn

+0

@sotn:これは同じ質問ではないようです。私はそこにこれと矛盾するような答えは見ません。あなたは、あなたが話している答えに直接リンクし、それがこの「反対」であることを説明できますか? – StriplingWarrior

6

ユニットテストでDIコンテナを使用しないようにしてください。単体テストでは、1つのクラスまたはモジュールを単独でテストしようとします。その領域のDIコンテナにはほとんど使用されません。

システム内のコンポーネントがどのように統合され、連携して動作するかをテストする必要があるため、統合テストでは違いがあります。その場合、多くの場合、プロダクションDI設定を使用して、偽のサービス(MailServiceなど)のためにいくつかのサービスをスワップしますが、可能な限り本物に近づけます。この場合、コンテナを使用してオブジェクトグラフ全体を解決します。

単体テストでDIコンテナを使用したいという願望は、しばしば無効なパターンに由来します。たとえば、テスト中のすべての依存関係を持つテスト対象のクラスを作成しようとすると、重複した初期化コードが大量に発生し、テスト中のクラスの変更がシステム全体に波及する可能性があります。数十のユニットテストを変更します。これは明らかに保守性の問題を引き起こします。

過去に私をここで多く助けてくれた1つのパターンは、単純なテストクラス固有のファクトリメソッドの使用です。このメソッドは、テスト中のクラスの作成を集中化し、テスト中のクラスの依存関係が変更されたときに行う必要がある変更の量を最小限に抑えます。

private ClassUnderTest CreateValidClassUnderTest(params object[] dependencies) { 
    return new ClassUnderTest(
     dependencies.OfType<ILogger>().SingleOrDefault() ?? new FakeLogger(), 
     dependencies.OfType<IMailSender>().SingleOrDefault() ?? new FakeMailer(), 
     dependencies.OfType<IEventPublisher>().SingleOrDefault() ?? new FakePublisher()); 
} 

このファクトリメソッドは、任意の依存関係を受け入れるparams配列が含まれています。これは次のようなファクトリメソッドが見ることができる方法です。コードはリストから依存関係を抽出し、特定の依存関係が見つからない場合は、新しい偽実装が挿入されます。

これはほとんどのテストで1つまたは2つの依存関係に関心があるためです。他の依存関係は、クラスが機能するために必要ですが、特定のテストでは興味がありません。したがって、ファクトリメソッドは、未使用の依存関係のノイズを除去しながら、テストの対象となる依存関係のみを提供することができます。ファクトリメソッドは、したがって、次のテストを書くことがことができます:

public void Test() { 
    // Arrange 
    var logger = new ListLogger(); 

    var cut = CreateValidClassUnderTest(logger); 

    // Act 
    cut.DoSomething(); 

    // Arrange 
    Assert.IsTrue(logger.Count > 0);  
} 

あなたが読み取り可能な信頼できると保守性テストの書き方を学ぶことに興味があるなら、私はロイOsheroveの本ユニットテストのアート(第二版を読むためにあなたをアドバイス)。これは私を大いに助けてくれました。

関連する問題