2

我目前正在开发的一个数据库应用程序将各种设置存储在数据库中。这些设置中的大多数用于自定义某些业务规则,但其中也有一些其他的东西。

该应用程序包含专门执行某项任务的对象,例如某项复杂的计算。这些非 UI 对象是经过单元测试的,但也需要访问许多全局设置。我们现在实现这一点的方式是提供由应用程序控制器在运行时填充的对象属性。测试时,我们在测试中创建对象并填写测试值(不是来自数据库)。

这比让所有这些对象都需要一些全局设置对象更好——这当然有效地使单元测试变得不可能:) 缺点可能是您有时需要设置十几个属性,或者您需要让这些属性“渗透”到子对象中。

所以一般的问题是:你如何在你的项目中提供对全局应用程序设置的访问,而不需要全局变量,同时仍然能够对你的代码进行单元测试?这一定是一个已经解决了 100 次的问题......

(注意:我不是一个经验丰富的程序员,你会注意到的;但我喜欢学习!当然,我已经对这个主题进行了研究,但我真的想先找一些-手经验)

4

4 回答 4

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);

它是一个可以交换用于测试目的的单例存储库。

于 2008-08-15T12:25:19.483 回答
1

我喜欢使用服务定位器模式来模拟我的配置访问。这给了我一个点来获取我需要的任何配置值,并将其放在应用程序之外的单独库中,它允许重用和可测试性。这是一些示例代码,我不确定您使用的是什么语言,但我是用 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);
    }
}

希望这可以帮助。

于 2008-08-15T12:57:22.337 回答
0

通常这是由 ini 文件或 XML 配置文件处理的。然后你就有了一个在需要时读取设置的类。

.NET 内置了 ConfigurationManager 类,但它很容易实现,只需读取文本文件,或将 XML 加载到 DOM 或在代码中手动解析它们。

在数据库中有配置文件是可以的,但它确实将您与数据库联系起来,并为您的应用程序创建了 ini/xml 文件解决的额外依赖关系。

于 2008-08-15T11:44:52.217 回答
0

我这样做了:

public class MySettings
{
    public static double Setting1
        { get { return SettingsCache.Instance.GetDouble("Setting1"); } }

    public static string Setting2
        { get { return SettingsCache.Instance.GetString("Setting2"); } }
}

我把它放在一个单独的基础设施模块中,以消除循环依赖的任何问题。
这样做我不依赖于任何特定的配置方法,并且在我的应用程序代码中没有运行严重破坏的字符串。

于 2008-08-15T12:20:26.450 回答