私は、モックの方法がわからないというコードのこの時点で立ち往生しています:
ConfigurationManager.AppSettings["User"];
ConfigurationManagerをモックする必要がありますが、手がかりがありません。 Moq を使用しています。
誰か教えてもらえますか?ありがとう!
これに対する標準的なアプローチの1つは、facadeパターンを使用して構成マネージャーをラップすることであると考えています。
したがって、ConfigurationManagerをラップします。何かのようなもの:
public class Configuration: IConfiguration
{
public User
{
get{
return ConfigurationManager.AppSettings["User"];
}
}
}
(構成クラスからインターフェイスを抽出し、そのインターフェイスをコードのすべての場所で使用できます)次に、IConfigurationをモックします。いくつかの異なる方法でファサード自体を実装できる場合があります。上記では、個々のプロパティをラップすることのみを選択しました。また、弱く型付けされたハッシュ配列ではなく、強く型付けされた情報が機能するという副次的な利点も得られます。
AspnetMvc4を使用しています。ちょっと前に書きました
ConfigurationManager.AppSettings["mykey"] = "myvalue";
私のテスト方法では完璧に機能しました。
説明:テストメソッドは、アプリ設定(通常はweb.config
またはmyapp.config
)から取得したコンテキストで実行されます。 ConfigurationsManager
は、このアプリケーショングローバルオブジェクトに到達して操作できます。
ただし:テストランナーが並行してテストを実行している場合、これはお勧めできません。
たぶんあなたが達成する必要があるものではありませんが、テストプロジェクトでapp.configを使用することを検討しましたか?そのため、ConfigurationManagerはapp.configに入れた値を取得し、モックする必要はありません。このソリューションは、「可変」構成ファイルをテストする必要がないため、私のニーズに適しています。
シムを使用して、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.
}
}
シムと偽物の詳細については、 Microsoft偽物によるテスト中のコードの分離 を参照してください。お役に立てれば。
モックではなくスタブを検討しましたか? 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に依存しないことです。
これは静的プロパティであり、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; } }
}