我目前正在处理的数据库应用程序将各种设置存储在数据库中。大多数设置都是为了定制某些业务规则,但也有一些其他的东西。
该应用包含专门执行特定任务的对象,例如某个复杂的计算。这些非UI对象经过单元测试,但也需要访问大量的全局设置。我们现在实现此方法的方法是在运行时提供由Application Controller填充的对象属性。在测试时,我们在测试中创建对象并填入测试值(而不是从数据库中)。
这样做效果更好,无论如何要比所有这些对象都需要一些全局设置对象好得多 - 这当然有效地使单元测试变得不可能:)缺点可能是你有时需要设置了十几个属性,或者你需要让这些属性“渗透”为子对象。
所以一般的问题是:如何在不需要全局变量的情况下提供对项目中全局应用程序设置的访问,同时仍然能够对代码进行单元测试?这必须是一个已经解决了100次的问题......
(注意:我并不是一个经验丰富的程序员,因为你会注意到;但我喜欢学习!当然,我已经对这个话题进行了研究,但我真的很期待对于一些第一手经验)
答案 0 :(得分:1)
您可以使用Martin Fowlers ServiceLocator模式。在PHP中它可能看起来像这样:
class ServiceLocator {
private static $soleInstance;
private $globalSettings;
public static function load($locator) {
self::$soleInstance = $locator;
}
public static function globalSettings() {
if (!isset(self::$soleInstance->globalSettings)) {
self::$soleInstance->setGlobalSettings(new GlobalSettings());
}
return self::$soleInstance->globalSettings;
}
}
您的生产代码然后初始化服务定位器,如下所示:
ServiceLocator::load(new ServiceLocator());
在您的测试代码中,您可以插入如下的模拟设置:
ServiceLocator s = new ServiceLocator();
s->setGlobalSettings(new MockGlobalSettings());
ServiceLocator::load(s);
它是单个人的存储库,可以交换用于测试目的。
答案 1 :(得分:1)
我喜欢模拟Service Locator模式的配置访问。这给了我一点点来获取我需要的任何配置值,并将它放在一个单独的库中的应用程序之外,它允许重用和可测试性。这是一些示例代码,我不确定您使用的是哪种语言,但我是用C#编写的。
首先,我创建一个将为我的ConfigurationItem建模的泛型类。
public class ConfigurationItem<T>
{
private T item;
public ConfigurationItem(T item)
{
this.item = item;
}
public T GetValue()
{
return item;
}
}
然后我创建一个类,为配置项公开公共静态只读变量。这里我只是从配置文件中读取ConnectionStringSettings,它只是xml。当然,对于更多项目,您可以从任何来源读取值。
public class ConfigurationItems
{
public static ConfigurationItem<ConnectionStringSettings> ConnectionSettings = new ConfigurationItem<ConnectionStringSettings>(RetrieveConnectionString());
private static ConnectionStringSettings RetrieveConnectionString()
{
// In .Net, we store our connection string in the application/web config file.
// We can access those values through the ConfigurationManager class.
return ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["ConnectionKey"]];
}
}
然后,当我需要使用ConfigurationItem时,我称之为:
ConfigurationItems.ConnectionSettings.GetValue();
它将返回一个类型安全值,然后我可以缓存或执行我想要的任何操作。
以下是一个示例测试:
[TestFixture]
public class ConfigurationItemsTest
{
[Test]
public void ShouldBeAbleToAccessConnectionStringSettings()
{
ConnectionStringSettings item = ConfigurationItems.ConnectionSettings.GetValue();
Assert.IsNotNull(item);
}
}
希望这有帮助。
答案 2 :(得分:0)
通常这由ini文件或XML配置文件处理。然后你就有了一个在需要时读取设置的类。
.NET内置了ConfigurationManager类,但它很容易实现,只需读取文本文件,或者将XML加载到DOM中或手动解析代码。
在数据库中配置文件是可以的,但它确实将您绑定到数据库,并为您的应用程序创建了一个额外的依赖性,即ini / xml文件解决。
答案 3 :(得分:0)
我这样做了:
public class MySettings
{
public static double Setting1
{ get { return SettingsCache.Instance.GetDouble("Setting1"); } }
public static string Setting2
{ get { return SettingsCache.Instance.GetString("Setting2"); } }
}
我把它放在一个单独的基础架构模块中,以消除循环依赖的任何问题 这样做我没有绑定任何特定的配置方法,也没有在我的应用程序代码中运行严重的字符串。