moqでConfigurationManager.AppSettingsをモックする方法


122

私はモックする方法がわからないコードのこの時点で行き詰まっています:

ConfigurationManager.AppSettings["User"];

ConfigurationManagerをモックする必要がありますが、手掛かりがなく、Moqを使用しています

誰かが私にチップを与えることができますか?ありがとう!

回答:


103

これに対する標準的なアプローチの1つは、ファサードパターンを使用して構成マネージャーをラップすることです。そうすれば、制御できる何かが疎結合になります。

したがって、ConfigurationManagerをラップします。何かのようなもの:

public class Configuration: IConfiguration
{
    public User
    {
        get
        { 
            return ConfigurationManager.AppSettings["User"];
        }
    }
}

(構成クラスからインターフェイスを抽出して、そのインターフェイスをコードのあらゆる場所で使用できます)次に、IConfigurationをモックします。ファサード自体をいくつかの異なる方法で実装できる場合があります。上記では、個々のプロパティをラップするためだけに選択しました。また、弱く型付けされたハッシュ配列ではなく、強く型付けされた情報を処理できるという副次的な利点もあります。


6
これも概念的に私がやっていることです。ただし、インターフェイスの実装をオンザフライで生成するCastle DictionaryAdapter(Castle Coreの一部)を使用しています。私はそれについて少し前に書いています:blog.andreloker.de/post/2008/09/05/…(「A Solution」までスクロールして、Castle DictionaryAdapterの使用方法を確認してください)
Andre Loker

それは気の利いた、良い記事です。これを将来のために覚えておかなければならない。
ジョシュアエンフィールド

私はまた追加するかもしれません-あなたの純粋主義と解釈に応じて-これは代わりに、または代理プロキシまたはアダプタと呼ばれることもできます。
ジョシュアエンフィールド

3
上からは、Moqを「通常」として使用しているだけです。未テストですが、次のようなものです。var configurationMock = new Mock<IConfiguration>();セットアップについて:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Joshua Enfield

このシナリオは、レイヤーがIConfigurationに依存していて、IConfigurationをモックする必要がある場合に使用されますが、IConfiguration実装をどのようにテストしますか?また、ユニットテストでConfigurationManager.AppSettings ["User"]を呼び出すと、ユニットはテストされませんが、構成ファイルからフェッチされる値はテストされますが、これはユニットテストではありません。実装を確認する必要がある場合は、@ zpbappi.com/testing-codes-with-configurationmanager-appsettings
nkalfovを

172

AspnetMvc4を使用しています。少し前に書いた

ConfigurationManager.AppSettings["mykey"] = "myvalue";

私のテスト方法では、それは完全に機能しました。

説明:テストメソッドは、アプリの設定(通常はweb.configまたは)から取得したコンテキストで実行されますmyapp.configConfigurationsManagerこのアプリケーショングローバルオブジェクトに到達して操作できます。

ただし:テストランナーが並行してテストを実行している場合、これは良いアイデアではありません。


8
これは問題を解決するための本当に賢明で簡単な方法です!シンプルさに対する称賛!
Navap

1
ほとんどの場合、抽象化を作成するよりもはるかに簡単です
Michael Clark

2
それでおしまい????この特定の封印されたクラスをテストする方法に頭を悩ませていたので、素晴らしさは単純さにあります。
Piotr Kula 2017

5
ConfigurationManager.AppSettingsNameValueCollectionスレッドセーフではないので、適切な同期なしにそれを使用して並行テストを行うことは、とにかく良い考えではありません。それ以外の場合ConfigurationManager.AppSettings.Clear()は、TestInitialize/ ctorを呼び出すだけで十分です。
Ohad Schneider 2017

1
シンプルで簡潔。断然最良の答えです!
znn

21

たぶん、あなたが達成する必要があるものではありませんが、テストプロジェクトでapp.configを使用することを検討しましたか?したがって、ConfigurationManagerはapp.configに入力した値を取得し、何もモックする必要はありません。「可変」構成ファイルをテストする必要がないため、このソリューションは私のニーズにうまく機能します。


7
テスト対象のコードの動作が構成値の値に応じて変化する場合、AppSettingsに直接依存しないのであれば、コードをテストする方が確かに簡単です。
Andre Loker

2
他の可能な設定をテストすることはないため、これは悪い習慣です。ジョシュアエンフィールドの答えはテストに最適です。
mkaj 2015年

4
他の人はこの答えに反対していますが、彼らの立場は少し一般化されていると思います。これはいくつかのシナリオで非常に有効な答えであり、それは本当にあなたが必要としているものに依存します。たとえば、4つの異なるクラスターがあり、それぞれに異なるベースURLがあるとします。これらの4つのクラスターは、実行時にWeb.configプロジェクトを取り巻くものからプルされます。テスト中、よく知られている値をから取得することapp.configは非常に有効です。単体テストでは、「cluster1」がプルされたときの条件が機能することを確認する必要があります。この場合、クラスタは4つしかありません。
Mike Perrenoud 2015年

14

シムを使用AppSettingsしてカスタムNameValueCollectionオブジェクトを変更できます。これを実現する方法の例を次に示します。

[TestMethod]
public void TestSomething()
{
    using(ShimsContext.Create()) {
        const string key = "key";
        const string value = "value";
        ShimConfigurationManager.AppSettingsGet = () =>
        {
            NameValueCollection nameValueCollection = new NameValueCollection();
            nameValueCollection.Add(key, value);
            return nameValueCollection;
        };

        ///
        // Test code here.
        ///

        // Validation code goes here.        
    }
}

シムと偽物の詳細については、Isolating Code Under Test with Microsoft Fakesを参照してください。お役に立てれば。


6
著者は、MS Fakesについてではなく、moqを使用する方法を求めています。
JPCF 2013年

6
そして、これはどう違うのですか?それは彼のコードからデータの依存関係を取り除くことによってモックを実現します。C#の偽物を使用するのも1つの方法です。
Zorayr 2013年

9

あざけるのではなくスタブすることを考えましたか?AppSettingsプロパティがありますNameValueCollection

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        // Arrange
        var settings = new NameValueCollection {{"User", "Otuyh"}};
        var classUnderTest = new ClassUnderTest(settings);

        // Act
        classUnderTest.MethodUnderTest();

        // Assert something...
    }
}

public class ClassUnderTest
{
    private readonly NameValueCollection _settings;

    public ClassUnderTest(NameValueCollection settings)
    {
        _settings = settings;
    }

    public void MethodUnderTest()
    {
        // get the User from Settings
        string user = _settings["User"];

        // log
        Trace.TraceInformation("User = \"{0}\"", user);

        // do something else...
    }
}

利点は、実装がよりシンプルになり、本当に必要になるまでSystem.Configurationに依存しないことです。


3
私はこのアプローチが一番好きです。一方ではIConfiguration、Joshua Enfieldが示唆するように、構成マネージャーをas でラップするのは高レベルである可能性があり、構成値の解析の誤りなどが原因で存在するバグを見逃す可能性があります。一方、ConfigurationManager.AppSettingsLosManosが示唆するように直接使用することは、実装の詳細が多すぎることは言うまでもありません。言うまでもなく、他のテストに副作用があり、手動で同期しないと並列テスト実行で使用できません(NameValueConnectionスレッドセーフではないため)。
Ohad Schneider 2017

2

これは静的プロパティであり、Moqは継承によってモックできるMoqインスタンスメソッドまたはクラス向けに設計されています。言い換えれば、Moqはここでは何の助けにもなりません。

静態をモックするために、私はMolesと呼ばれる無料のツールを使用します。Typemockなど、これを実行できるフレームワーク分離ツールは他にもありますが、有料のツールだと思います。

静的およびテストに関しては、別のオプションは静的状態を自分で作成することですが、これはしばしば問題になる可能性があります(あなたの場合はそうなると思います)。

そして最後に、分離フレームワークがオプションではなく、このアプローチに専念している場合、Joshuaによって言及されているファサードは優れたアプローチ、またはこれのクライアントコードをビジネスロジックから分離する一般的なアプローチですテストに使用しています。


1

独自のapp.configプロバイダーを作成するのは簡単な作業であり、他の何よりも便利だと思います。特に、シムなどの偽物は使用しないでください。それらを使用するとすぐに、編集と続行が機能しなくなります。

私が使用するプロバイダーは次のようになります。

デフォルトでは、それらはから値を取得しますApp.configが、ユニットテストの場合、すべての値をオーバーライドして、各テストで個別に使用できます。

何度も何度もインターフェースを実装したり、実装する必要はありません。ユーティリティdllがあり、この小さなヘルパーを多くのプロジェクトや単体テストで使用しています。

public class AppConfigProvider
{
    public AppConfigProvider()
    {
        ConnectionStrings = new ConnectionStringsProvider();
        AppSettings = new AppSettingsProvider();
    }

    public ConnectionStringsProvider ConnectionStrings { get; private set; }

    public AppSettingsProvider AppSettings { get; private set; }
}

public class ConnectionStringsProvider
{
    private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    public string this[string key]
    {
        get
        {
            string customValue;
            if (_customValues.TryGetValue(key, out customValue))
            {
                return customValue;
            }

            var connectionStringSettings = ConfigurationManager.ConnectionStrings[key];
            return connectionStringSettings == null ? null : connectionStringSettings.ConnectionString;
        }
    }

    public Dictionary<string, string> CustomValues { get { return _customValues; } }
}

public class AppSettingsProvider
{
    private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    public string this[string key]
    {
        get
        {
            string customValue;
            return _customValues.TryGetValue(key, out customValue) ? customValue : ConfigurationManager.AppSettings[key];
        }
    }

    public Dictionary<string, string> CustomValues { get { return _customValues; } }
}

1

必要なものを設定するだけではどうですか?.NETをモックしたくないので、私は...?

System.Configuration.ConfigurationManager.AppSettings["myKey"] = "myVal";

おそらく、事前にAppSettingsをクリーンアップして、アプリが必要なものだけを表示するようにする必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.