如何使用 moq 模拟 ConfigurationManager.AppSettings

我被困在这个我不知道如何嘲笑的代码点:

ConfigurationManager.AppSettings["User"];

我必须嘲笑 ConfigurationManager,但是我不知道,我正在使用 莫克

有人能给我点小费吗? 谢谢!

95430 次浏览

我相信这方面的一个标准方法是使用 正面模式来包装配置管理器,然后您可以控制松散耦合的内容。

你可以包装 ConfigurationManager,比如:

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

(您可以从配置类中提取一个接口,然后在代码中的任何地方使用该接口) 然后模仿 IConfiguration。您可以通过几种不同的方式实现 facade 本身。上面我选择只包装单个属性。您还可以获得使用强类型信息而不是弱类型哈希数组的副作用。

也许这不是您需要完成的任务,但是您是否考虑过在您的测试项目中使用 app.config? 因此,ConfigurationManager 将获得您放入 app.config 中的值,并且您不需要模拟任何东西。 这个解决方案非常适合我的需要,因为我从来不需要测试“变量”配置文件。

这是一个静态属性,Moq 被设计成可以通过继承模拟的 Moq 实例方法或类。换句话说,莫克在这里帮不了你。

对于模拟静态,我使用一个名为 鼹鼠的工具,它是免费的。还有其他框架隔离工具,比如 Tyemock 也可以做到这一点,尽管我相信这些都是付费工具。

当涉及到静态和测试时,另一种选择是自己创建静态状态,尽管这通常会有问题(我想在您的例子中也会出现这种情况)。

最后,如果隔离框架不是一个选项,并且您致力于这种方法,那么 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.
}
}

你可以在 用 MicrosoftFakes 隔离正在测试的代码上读到更多关于垫片和假货的信息,希望对你有所帮助。

我正在使用 AspnetMvc4

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

在我的测试方法和它完美地工作。

说明: 测试方法运行在具有应用程序设置的上下文中,通常是 web.configmyapp.configConfigurationsManager可以到达这个应用程序全局对象并对其进行操作。

但是: 如果您有一个并行运行测试的测试运行程序,这不是一个好主意。

你有没有考虑过用撞桩代替嘲笑? 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; } }
}

恐怕我得回想一下我说过的话。配置管理器。AppSettings 偶尔会表现得很奇怪,比如它不会总是立即生成刚刚写入的值。由于这个原因,我们的构建机器上有零星的单元测试失败。我必须重写代码以使用包装器,并返回 ConfigurationManager。通常情况下的 AppSettings 和单元测试中的测试值。

为什么不直接设置你需要的呢? 因为,我不想嘲笑.NET,我... ... ?

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

您可能应该事先清除 AppSettings,以确保应用程序只能看到您希望它看到的内容。

实现这个目标的另一种方法是提供你自己的 IConfiguration,从任何你希望它从中提取的文件中提取,像这样:

var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();

现在,只要在这个 JSON 文件中有测试所需的值,就很容易覆盖和更改值。