回答:
これに対する標準的なアプローチの1つは、ファサードパターンを使用して構成マネージャーをラップすることです。そうすれば、制御できる何かが疎結合になります。
したがって、ConfigurationManagerをラップします。何かのようなもの:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(構成クラスからインターフェイスを抽出して、そのインターフェイスをコードのあらゆる場所で使用できます)次に、IConfigurationをモックします。ファサード自体をいくつかの異なる方法で実装できる場合があります。上記では、個々のプロパティをラップするためだけに選択しました。また、弱く型付けされたハッシュ配列ではなく、強く型付けされた情報を処理できるという副次的な利点もあります。
var configurationMock = new Mock<IConfiguration>();
セットアップについて:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
AspnetMvc4を使用しています。少し前に書いた
ConfigurationManager.AppSettings["mykey"] = "myvalue";
私のテスト方法では、それは完全に機能しました。
説明:テストメソッドは、アプリの設定(通常はweb.config
または)から取得したコンテキストで実行されますmyapp.config
。ConfigurationsManager
このアプリケーショングローバルオブジェクトに到達して操作できます。
ただし:テストランナーが並行してテストを実行している場合、これは良いアイデアではありません。
ConfigurationManager.AppSettings
はNameValueCollection
スレッドセーフではないので、適切な同期なしにそれを使用して並行テストを行うことは、とにかく良い考えではありません。それ以外の場合ConfigurationManager.AppSettings.Clear()
は、TestInitialize
/ ctorを呼び出すだけで十分です。
たぶん、あなたが達成する必要があるものではありませんが、テストプロジェクトでapp.configを使用することを検討しましたか?したがって、ConfigurationManagerはapp.configに入力した値を取得し、何もモックする必要はありません。「可変」構成ファイルをテストする必要がないため、このソリューションは私のニーズにうまく機能します。
Web.config
プロジェクトを取り巻くものからプルされます。テスト中、よく知られている値をから取得することapp.config
は非常に有効です。単体テストでは、「cluster1」がプルされたときの条件が機能することを確認する必要があります。この場合、クラスタは4つしかありません。
シムを使用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を参照してください。お役に立てれば。
あざけるのではなくスタブすることを考えましたか?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に依存しないことです。
IConfiguration
、Joshua Enfieldが示唆するように、構成マネージャーをas でラップするのは高レベルである可能性があり、構成値の解析の誤りなどが原因で存在するバグを見逃す可能性があります。一方、ConfigurationManager.AppSettings
LosManosが示唆するように直接使用することは、実装の詳細が多すぎることは言うまでもありません。言うまでもなく、他のテストに副作用があり、手動で同期しないと並列テスト実行で使用できません(NameValueConnection
スレッドセーフではないため)。
これは静的プロパティであり、Moqは継承によってモックできるMoqインスタンスメソッドまたはクラス向けに設計されています。言い換えれば、Moqはここでは何の助けにもなりません。
静態をモックするために、私はMolesと呼ばれる無料のツールを使用します。Typemockなど、これを実行できるフレームワーク分離ツールは他にもありますが、有料のツールだと思います。
静的およびテストに関しては、別のオプションは静的状態を自分で作成することですが、これはしばしば問題になる可能性があります(あなたの場合はそうなると思います)。
そして最後に、分離フレームワークがオプションではなく、このアプローチに専念している場合、Joshuaによって言及されているファサードは優れたアプローチ、またはこれのクライアントコードをビジネスロジックから分離する一般的なアプローチですテストに使用しています。
独自の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; } }
}