当处理基类和派生类时,处理AppSettings的最佳方式是什么,这将允许可测试性
本文关键字:处理 是什么 方式 可测试性 最佳 AppSettings 基类 派生 | 更新日期: 2023-09-27 18:08:26
我希望这是一个体面的问题,背景是我有一个抽象基类,这个基类有一些具体的方法实现,可以访问派生类。基类和所有派生类都有一组唯一的变量。所有常见的功能都在基类的具体方法中(这些方法不是虚拟的,所以它们不能被覆盖)。每个派生类也从App.Config文件中的AppSettings中获得一些这些值。
所以我的问题是,哪里是最好的地方,把这些const值,将遵循最佳实践,并允许代码是适当的可测试的(这最后一块是非常重要的)?
我知道的两个选项是:
1)创建一个静态配置类,其中包含每个类的所有值。这对于正常的const来说是很好的,但是检索App.Config值将失败,除非测试项目在自己的App.Config文件中有这些值(对我来说这似乎是一个肮脏的hack)
2)另一种选择是声明它们并在using类中获得值设置,这坚持在使用它的地方声明它原则,但它使类不可测试,除非您再次将值添加到测试项目的App.Config中。
public abstract class BaseClassA
{
private const string PATH = "'ParentClassPath'" // Option 2
private int _var1;
private string _var2;
public BaseClassA(int param1, string param2)
{
_var1 = param1;
_var2 = param2;
}
public int Param1Prop { get; private set;}
public string Param2Prop { get; private set; }
protected string Method1(string value1, string value2, string value3)
{
Directory.CreateDirectory(StaticClass.PATH); //Option 1
return Path.GetDirectoryName(PATH) //Option 2
}
}
public class DerivedClassB
: base(1, "param2")
{
private const string PATH = "'DerivedClassPath'" // Option 2
public BaseClassA(int param1, string param2)
{
_var1 = param1;
_var2 = param2;
}
public int Param1Prop { get; private set;}
public string Param2Prop { get; private set; }
protected string Method1(string value1, string value2, string value3)
{
Directory.CreateDirectory(StaticClass.DERIVED_PATH); //Option 1
return Path.GetDirectoryName(PATH) //Option 2
}
}
创建一个自定义类来封装对ConfigurationManager的调用。AppSettings["key"]带有一个接口。在测试中模拟该接口,以便可以定义需要测试的值。要解释的粗略示例(未经测试):
public interface IConfigurationService
{
string Get(string key);
}
public class ConfigurationService :IConfigurationService
{
public string Get(string key)
{
return ConfigurationManager.AppSettings[key];
}
}
在您的测试中,您现在可以这样模拟您的接口:
public void NaiveTest()
{
var key = "someKey";
var result = "someValue";
var mockConfigurationService = new Mock<IConfigurationService>();
mockConfigurationService.Setup(x => x.Get(key)).Returns(result);
// pass service to class being tested and continue
}
在基类中包含配置服务,然后每个派生类可以根据需要提取它们的值。现在可以通过使用mock测试任何值。更多链接:https://github.com/moq/moq4
从测试的角度来看,这听起来像是Fakes的工作。您可以添加一个shim来拦截读取配置值的调用,然后您可以在测试中编写多个可能的值,而根本不需要配置文件。
这是一篇关于它们如何工作的文章。https://msdn.microsoft.com/en-us/library/hh549175.aspx
这里有一个部分的例子,如果你试图从注册表拦截读取,它将如何在你的单元测试中看起来:(我没有任何使用配置文件的例子,但你得到的想法。)
using (ShimsContext.Create())
{
Microsoft.Win32.Fakes.ShimRegistryKey.AllInstances.GetValueString = (key, valueName) =>
{ return "SomeValue"; };
Microsoft.Win32.Fakes.ShimRegistryKey.AllInstances.OpenSubKeyStringBoolean = (key, subkey, write) =>
{
var openKey = new Microsoft.Win32.Fakes.ShimRegistryKey();
openKey.NameGet = () => Path.Combine(key.Name, subkey);
return openKey;
};
// Exercise the code under test that reads from the registry here.
// Make Assertions
}
所以在这个例子中,当代码试图调用注册表来打开注册表项时,它会运行OpenSubKeyStringBoolean lambda。然后,当它对返回的键调用GetValue时,它会运行GetValueString lambda。所以结果是"SomeValue"被返回,而不是在注册表项(或不是)。
使用ShimsContext中的任何内容都运行您的shim代码。一旦ShimsContext被处理掉,一切就会恢复正常。