web-dev-qa-db-ja.com

シングルトンを使用した単体テスト

Visual Studio Team Editionのテストフレームワークでいくつかの自動テストを準備しました。プログラムで行われる通常の方法に従って、テストの1つがデータベースに接続するようにします。

string r_providerName = ConfigurationManager.ConnectionStrings["main_db"].ProviderName;

しかし、私はこの行で例外を受け取っています。これは、ConfigurationManagerがシングルトンであるために発生していると思われます。単体テストでシングルトンの問題をどのように回避できますか?


返信いただきありがとうございます。それらはすべて非常に有益でした。

45
yeyeyerman
79
Gregory Pakosz

コンストラクター依存性注入を使用できます。例:

public class SingletonDependedClass
{
    private string _ProviderName;

    public SingletonDependedClass()
        : this(ConfigurationManager.ConnectionStrings["main_db"].ProviderName)
    {
    }

    public SingletonDependedClass(string providerName)
    {
        _ProviderName = providerName;
    }
}

これにより、テスト中に接続文字列をオブジェクトに直接渡すことができます。

また、Visual Studio Team Editionテストフレームワークを使用する場合は、パラメーターをプライベートにしてコンストラクターを作成し、アクセサーを介してクラスをテストできます。

実際、私はそのような問題をあざけることで解決します。例:

シングルトンに依存するクラスがあります:

public class Singleton
{
    public virtual string SomeProperty { get; set; }

    private static Singleton _Instance;
    public static Singleton Insatnce
    {
        get
        {
            if (_Instance == null)
            {
                _Instance = new Singleton();
            }

            return _Instance;
        }
    }

    protected Singleton()
    {
    }
}

public class SingletonDependedClass
{
    public void SomeMethod()
    {
        ...
        string str = Singleton.Insatnce.SomeProperty;
        ...
    }
}

まず、SingletonDependedClassインスタンスをリファクタリングして、Singletonインスタンスをコンストラクターパラメーターとして取得する必要があります。

public class SingletonDependedClass
{    
    private Singleton _SingletonInstance;

    public SingletonDependedClass()
        : this(Singleton.Insatnce)
    {
    }

    private SingletonDependedClass(Singleton singletonInstance)
    {
        _SingletonInstance = singletonInstance;
    }

    public void SomeMethod()
    {
        string str = _SingletonInstance.SomeProperty;
    }
}

SingletonDependedClassのテスト( Moqモッキングライブラリ が使用されます):

[TestMethod()]
public void SomeMethodTest()
{
    var singletonMock = new Mock<Singleton>();
    singletonMock.Setup(s => s.SomeProperty).Returns("some test data");
    var target = new SingletonDependedClass_Accessor(singletonMock.Object);
    ...
}
13
bniwredyc

ブックからの例レガシーコードを効果的に使用する

ここにも同じ答えがあります: https://stackoverflow.com/a/28613595/929902

テストハーネスでシングルトンを含むコードを実行するには、シングルトンプロパティを緩和する必要があります。方法は次のとおりです。最初のステップは、シングルトンクラスに新しい静的メソッドを追加することです。このメソッドにより、シングルトンの静的インスタンスを置き換えることができます。これをsetTestingInstanceと呼びます。

public class PermitRepository
{
    private static PermitRepository instance = null;
    private PermitRepository() {}
    public static void setTestingInstance(PermitRepository newInstance)
    {
        instance = newInstance;
    }
    public static PermitRepository getInstance()
    {
        if (instance == null) {
            instance = new PermitRepository();
        }
        return instance;
    }
    public Permit findAssociatedPermit(PermitNotice notice) {
    ...
    }
    ...
}

セッターが用意できたので、PermitRepositoryのテストインスタンスを作成して設定できます。テストのセットアップで次のようなコードを書きたいと思います。

public void setUp() {
    PermitRepository repository = PermitRepository.getInstance();
    ...
    // add permits to the repository here
    ...
    PermitRepository.setTestingInstance(repository);
}
9
Teoman shipahi

ここで、より一般的な問題に直面しています。誤用すると、シングルトンはテスト可能性を妨げます。

私は 詳細な分析 分離設計のコンテキストでこの問題を実行しました。私のポイントを要約してみます:

  1. シングルトンが重要なグローバル状態を保持している場合、シングルトンを使用しないでください。これには、データベース、ファイルなどの永続ストレージが含まれます。
  2. シングルトンオブジェクトへの依存関係がクラス名から明らかでない場合は、依存関係を注入する必要があります。シングルトンインスタンスをクラスにインジェクトする必要があることは、パターンの誤った使用法を証明しています(ポイント1を参照)。
  3. シングルトンのライフサイクルは、アプリケーションのライフサイクルと同じであると想定されています。ほとんどのシングルトン実装は、遅延ロードメカニズムを使用して自身をインスタンス化します。これは簡単なことであり、ライフサイクルが変わることはほとんどありません。そうでない場合は、シングルトンを使用しないでください。
5