C# 如何使用moq模拟ConfigurationManager.AppSettings
我被困在我不知道如何模仿的代码的这一点上:C# 如何使用moq模拟ConfigurationManager.AppSettings,c#,unit-testing,moq,C#,Unit Testing,Moq,我被困在我不知道如何模仿的代码的这一点上: ConfigurationManager.AppSettings["User"]; 我不得不嘲笑ConfigurationManager,但我一点也不知道,我正在使用它 有人能给我小费吗?谢谢 我认为一种标准方法是使用facade模式来包装configuration manager,然后您可以控制松散耦合的东西 因此,您需要包装ConfigurationManager。比如: public class Configuration: IConfigur
ConfigurationManager.AppSettings["User"];
我不得不嘲笑ConfigurationManager,但我一点也不知道,我正在使用它
有人能给我小费吗?谢谢 我认为一种标准方法是使用facade模式来包装configuration manager,然后您可以控制松散耦合的东西 因此,您需要包装ConfigurationManager。比如:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(您可以从配置类中提取接口,然后在代码中的任何地方使用该接口)
然后,您只需模拟IConfiguration。您可以通过几种不同的方式实现外观本身。在上面,我选择只包装单个属性。您还可以获得使用强类型信息而不是弱类型散列数组的附带好处。可能不是您需要完成的,但是您是否考虑过在测试项目中使用app.config? 因此ConfigurationManager将获得您在app.config中输入的值,您无需模拟任何内容。
这个解决方案非常适合我的需要,因为我从来不需要测试“变量”配置文件。这是一个静态属性,Moq设计用于Moq实例方法或类,可以通过继承进行模拟。换句话说,Moq在这里对你没有任何帮助 对于模拟静态,我使用了一个名为的工具,它是免费的。还有其他框架隔离工具,比如Typemock也可以做到这一点,尽管我相信这些是付费工具 当涉及到静态和测试时,另一种选择是自己创建静态状态,尽管这通常会有问题(我想在您的情况下会是这样)
最后,如果隔离框架不是一种选择,并且您致力于这种方法,Joshua提到的facade是一种很好的方法,或者任何通常的方法,其中您将客户机代码从用于测试的业务逻辑中分离出来。您可以使用垫片将
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.
}
}
有关垫片和赝品的更多信息,请访问。希望这有帮助 我正在使用AspnetMvc4。刚才我写了
ConfigurationManager.AppSettings["mykey"] = "myvalue";
在我的测试方法中,它非常有效
说明:测试方法在应用程序设置取自的上下文中运行,通常是web.config
或myapp.config
ConfigurationsManager
可以访问此应用程序全局对象并对其进行操作
不过:如果您有一个并行运行测试的测试运行程序,这不是一个好主意。您是否考虑过使用存根而不是模拟?
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。我认为编写自己的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; } }
}
公共类AppConfigProvider
{
公共AppConfigProvider()
{
ConnectionStrings=新的ConnectionStringsProvider();
AppSettings=新建AppSettingsProvider();
}
公共连接字符串提供程序连接字符串{get;private set;}
公共应用程序设置提供程序应用程序设置{get;private set;}
}
公共类连接字符串提供程序
{
私有只读字典_customValues=新字典(StringComparer.OrdinalIgnoreCase);
公共字符串此[字符串键]
{
得到
{
字符串自定义值;
if(_customValues.TryGetValue(键,out customValue))
{
返回自定义值;
}
var connectionStringSettings=ConfigurationManager.ConnectionStrings[key];
返回connectionStringSettings==null?null:connectionStringSettings.ConnectionString;
}
}
公共字典CustomValues{get{return}CustomValues;}
}
公共类应用程序设置提供程序
{
私有只读字典_customValues=新字典(StringComparer.OrdinalIgnoreCase);
公共字符串此[字符串键]
{
得到
{
字符串自定义值;
返回_customValues.TryGetValue(键,out customValue)?customValue:ConfigurationManager.AppSettings[键];
}
}
公共字典CustomValues{get{return}CustomValues;}
}
设置你需要的东西怎么样?因为,我不想嘲笑.NET,是吗
System.Configuration.ConfigurationManager.AppSettings["myKey"] = "myVal";
您可能应该事先清理应用程序设置,以确保应用程序只看到您希望它看到的内容。实现此目标的另一种方法是只提供您自己的
IConfiguration
,从您希望它从中提取的任何文件中提取,如下所示:
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();
现在,只要您在这个JSON文件中有测试所需的值,就可以很容易地重写和更改值。从概念上讲,这也是我正在做的。但是,我使用Castle DictionaryAdapter(核心的一部分),它动态生成接口的实现。我不久前就写过这篇文章:(向下滚动到“解决方案”,看看我是如何使用Castle DictionaryAdapter的)这篇文章很漂亮,也是一篇好文章。在将来,我必须记住这一点。我还可以添加—取决于您的纯粹主义和解释—这可以替代,也可以称为委托代理或适配器