Answers:
我相信一种标准的方法是使用外观模式包装配置管理器,然后您可以控制某些松散耦合的事物。
因此,您将包装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
/构造函数,你是金色的。
也许这不是您需要完成的,但是您是否考虑过在测试项目中使用app.config?因此,ConfigurationManager将获取您放入app.config中的值,并且您无需模拟任何内容。该解决方案可以很好地满足我的需求,因为我不需要测试“变量”配置文件。
Web.config
整个项目中拉出。在测试期间,从中提取一些众所周知的值app.config
是非常有效的。单元测试只需要确保“ cluster1”起作用的条件就可以起作用。在这种情况下,只有四个不同的集群。
您可以使用垫片将其修改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 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)的建议将配置管理器包装起来可能太高了,并且您可能会错过由于错误的配置值解析等问题而存在的错误。另一方面,ConfigurationManager.AppSettings
如LosManos所建议的那样直接使用太多的实现细节,更不用说它可能对其他测试产生副作用,并且如果没有手动同步就不能在并行测试运行中使用(因为NameValueConnection
这不是线程安全的)。
那是一个静态属性,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; } }
}