2

アプリケーションでこの Service Locator パターンを使用し、Singleton として実装しました。

サービス ロケータ パターン

そして今、私はそれをテストしたい.これまでのところ、私のクラスがシングルトンであることを確認するテストを書きました. 私もこのテストを書きました:

 [Test]
 [ExpectedException(typeof(ApplicationException))]
 public void GetService_Throws_Exception_When_Invalid_Key_Is_Provided()
 {
     locator.GetService<IRandomService>();
 }

しかし、IRandomService を使用するつもりはないので、最後のテストはあまり好きではありません。GetService<T>そのため、例外がスローされることをテストするためのより良い方法を探しています。また、このクラス用に記述できる関連テストが他にあるかどうかも知りたいです。

NUnitの最新バージョンを使用しています。

乾杯

4

3 に答える 3

4

いくつかのこと:

  • クラスがシングルトンであることを確認するテスト? シングルトン パターンは、シングルトンをインスタンス クラスとして扱おうとしてもコンパイルされないことを保証します。サービス ロケータを次のように記述していない場合、それは誤りです。

C# のシングルトン パターン:

public class MySingleton()
{
    //Could also be a readonly field, or private with a GetInstance method
    public static MySingleton Instance {get; private set;}
    static MySingleton()
    {
        Instance = new MySingleton();
    }
    private MySingleton() { ... }
} 

...

//in external code
var mySingletonInstanceRef = MySingleTon.Instance; //right
var mySingletonInstanceRef = new MySingleton(); //does not compile

//EDIT: The thread-safe lazy-loaded singleton
 public class MySingleton()
{
    //static fields with initializers are initialized on first reference, so this behaves lazily
    public static readonly MySingleton instance = new MySingleton();
    //instead of the below you could make the field public, or have a GetInstance() method
    public static MySingleton Instance {get{return instance;}

    private MySingleton() { ... }
} 
  • サービスロケーターはアンチパターンです。素晴らしいように聞こえますが、解決するために作成された問題を実際に解決するわけではありません。主な問題は、サービス ロケータと密接に結び付いていることです。ロケーターが変更されると、それを使用するすべてのクラスが変更されます。対照的に、依存性注入は凝ったフレームワークなしで行うことができます。複雑で、高価で、再利用可能などのオブジェクトが、コンストラクターを介して必要なオブジェクトに渡されるようにするだけです。DI/IoC フレームワークは、グラフ内のオブジェクトがその子の依存関係を認識できない場合でも、必要なオブジェクトの既知の依存関係がすべて提供されるようにすることで、このプロセスを合理化します。

  • クラスはすでに開発されています。TDD/BDD の精神は、まだ書いていないコードが正しいことを証明するテストを書くことです。今テストを書くことが目的を果たさないと言っているわけではありませんが、失敗したテストはオブジェクトを開いて修正する必要があり、コードがすでに統合されている場合は、他のものを壊す可能性があります.

  • 単体テストでは、本番環境で使用されることのない構造が多用されます。モック、スタブ、プロキシ、およびその他の「テスト ヘルパー」は、テスト対象のオブジェクトを、それが通常統合される環境から分離するために存在し、入力が A、B、および C の場合、テスト対象のオブジェクトが X を実行することを保証します。通常フックするものが A、B、および C を与えるかどうかです。したがって、本番環境では使用しないスケルトン インターフェイスのような単純な構造を作成することについて心配する必要はありません。テストケースで期待される入力を適切に表現している限り、問題ありません。

于 2011-02-16T18:44:46.763 に答える
2

しかし、. を使用するつもりはないので、最後のテストはあまり好きではありませんIRandomService

しかし、それは一種のポイントです。テスト セットアップ メソッドでロケーターを接続し (arrange)、接続されていないキーを検索し (act)、例外がスローされたことを確認します (アサート)。実際に使用するタイプを使用する必要はありません。メソッドが機能していることをある程度確信したいだけです。

また、このクラス用に作成できる関連テストが他にあるかどうかも知りたいです。

さて、ここで別の質問に答えます。

サービスロケーターパターンは悪ですか?

はい、純粋な悪です。

依存関係を明示的にしないため、依存関係の挿入の目的に反します (どのクラスでもサービス ロケーターから何かを引き出すことができます)。さらに、すべてのコンポーネントがこの 1 つのクラスに依存するようになります。

この 1 つのコンポーネントがコードベース全体に広がっているため、メンテナンスは信じられないほどの悪夢になります。あなたはこの 1 つのクラスに密接に結合されています。

さらに、テストは悪夢です。あなたが持っているとしましょう

public class Foo {
    public Foo() { // }
    public string Bar() { // }
}

そして、あなたはテストしたいですFoo.Bar

public void BarDoesSomething() {
    var foo = new Foo();
    Assert.Equal("Something", foo.Bar());
}

テストを実行すると例外が発生します

ServiceLocator could not resolve component Frob.

何?ああ、それはあなたのコンストラクタが次のようになっているからです:

public Foo() {
    this.frob = ServiceLocator.GetService<Frob>();
}

そしてどんどん。

避ける、避ける、避ける。

于 2011-02-16T18:41:34.943 に答える
1

あなたの質問がよくわかりません。本番環境では決して使用しない型を要求することに不満がありますか? サービス ロケーターを製品コードとは異なる方法で使用することに不満はありますか? テスト自体は問題ないように見えます。存在しないものを要求し、期待される動作が発生することを証明しています。単体テストの場合、そのようなことを行うのは完全に合理的です。

依存性注入コンテナーを使用するときに行ったことの 1 つは、アプリケーションの接続フェーズをモジュールに分割し、統合テストでルート タイプを解決して、アプリを接続できることを確認することでした。配線テストに失敗した場合は、アプリが機能していないという良い兆候でした (ただし、アプリが機能したことを証明したわけではありません)。サービス ロケータを使用しているときに、この種のことを行うのはトリッキーです。

私も Jason に 100% 同意します。サービス ロケーターは良いアイデアのように思えますが、すぐに厄介なものになります。それらは「プル」します (サービス ロケーター インスタンスを使用する型はそれに結合する必要があります) のに対し、依存性注入コンテナーは「プッシュ」します (アプリケーションの大部分は DI に依存しないため、コードははるかに脆くなく、再利用可能です)。

その他のいくつかのこと:

  1. [ExpectedException]廃止されました。代わりに Assert.Throws を使用してください
  2. ApplicationExceptionも非推奨です。
于 2011-02-16T18:48:47.300 に答える