0

创建用户配置文件的控制器正在使用配置文件视图模型,该模型的一部分是默认徽标 (PNG),如果用户选择不上传自己的,将显示和使用什么。

我创建public static class Configuration了保留项目的所有常量。常量之一是将用于默认徽标的文件名和位置。

我将实际文件名保留在 Web.Config 文件中,而不是硬编码常量 原因如下: 1. 文件名可以更改 2. 文件位置可以更改 3. 讨厌硬编码任何东西,当我知道它可能会更改时

第一次测试控制器的尝试在Web.Config通过调用访问的常量类上失败了ConfigurationManager.AppSettings。这里的人(非常公正地)告诉我这是另一个应该避免的依赖。另一方面,它只是一个外部化的常量,我不喜欢像这样硬编码File f = new File("..\\Images\\Profile\\DefaultLogo.PNG");。没有人做这些事情,它应该是可配置的(意思是Web.Config- 如果我错了,请纠正我)。

您对处理这种情况有何建议?

编辑问题 让我重申这个问题:它不是使用什么格式,而是如何处理让控制器 TDD 可测试并在架构上保持常量的困境。(目前我使用静态类,所以我必须在构造函数中手动传递控制器需要的所有常量,它看起来不是最好的选择)

  • 对常量进行硬编码(讨厌这样做)
  • 将它从控制器移动到视图模型(我认为这不会解决 TDD 问题)
  • IConfiguration在控制器中IRepository有另一个接口抽象(
  • 一些我没有想到的东西,因此寻求聪明的想法。

非常感谢

4

1 回答 1

2

在控制器中有另一个接口抽象(IConfiguration)以及 IRepository(对我来说看起来很难看) -而且它不起作用,因为 Config 类是静态的并且不能实现接口

大胆的说法并不完全正确。例如:

public interface IManageConfigurations
{
    string DefaultFileLocation { get; }
}

public class ConfigurationManagerImpl : IManageConfigurations
{
    public string DefaultFileLocation
    {
        get { return ConfigurationManager.AppSettings["DefaultFileLocation"]; }
    }
}

public class MyController : Controller
{
    private readonly IRepository _repository;
    private readonly IManageConfigurations _config;

    public MyController(IRepository repository, IManageConfigurations config)
    {
        _repository = repository;
        _config = config;
    }
}

我不认为这是一个丑陋的解决方案,但这主要是一种审美观点,这是主观的。在大多数情况下,我不认为一个类会显示过度注入反模式,直到它具有大约 5 个或更多接口依赖项。

此外,这几乎是单元可测试代码中依赖关系解析的标准解决方案。您可以像模拟存储库一样模拟配置界面。以上是我如何使用 web.config 中保存的值解决依赖关系。

ConfigurationManager.AppSettings["key"]另一种解决方案是在控制器中对调用进行硬编码。然后,在您的单元测试项目中,您可以在 app.config 文件中设置相同的值。然后,单元测试运行器将在 app.config 中查找值,而 Web 服务器将在 web.config 中查找它们。

public class MyController : Controller
{
    public ActionResult Index()
    {
        // the below will get the value from the unit test project's app.config
        // when run as a unit test, but will get the value from web.config in server
        var fileLocation = ConfigurationManager.AppSettings["DefaultFileLocation"];
    }
}

更新

如果你真的想将字符串保存在静态类中,并且有可单元测试的代码,你可以这样做:

public static class ConfigSettings
{
    public static string DefaultFileLocation
    {
        get { return ConfigurationManager.AppSettings["DefaultFileLocation"]; }
    }
}

然后,您可以在Controller. 但是,为了在作为单元测试运行时提供值,如上所述,您必须appSettings在单元测试项目的文件中放置一个节点,这与在 Web 项目的文件中放置节点的app.config方式非常相似。单元测试运行器将从 app.config 中提取值,而 Web 服务器将从 web.config 中提取值。appSettingsweb.config

UnitTestProject\app.config

<configuration>
    <appSettings>
        <!-- use this value when executed in unit test runner -->
        <add key="DefaultFileLocation"
            value="C:\Users\me\test_files\test_file.png" />
    </appSettings>
</configuration>

MvcProject\web.config(默认)

<configuration>
    <appSettings>
        <!-- use this value when executed locally in IIS Express -->
        <add key="DefaultFileLocation"
            value="C:\Users\me\Documents\Visual Studio 20xx\Projects\MyProj\App_Data\default_files\default_logo.png" />
    </appSettings>
</configuration>

MvcProject\web.Release.config(转换)

<configuration>
    <appSettings>
        <!-- use this value when executed on live IIS production server -->
        <add key="DefaultFileLocation"
            value="E:\approot\siteroot\App_Data\default_files\default_logo.png"
            xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
    </appSettings>
</configuration>

如果这不能回答你的问题,也许我还没有完全理解你在追求什么。

更新

我在您的解决方案中只缺少一件 - 如何在控制器中使用该公共静态类 ConfigSettings?我应该将此静态类作为参数传递给构造函数吗?

不,你不需要做任何特别的事情。只需将它用作一个类,就像使用 static 一样ConfigurationManager

public ActionResult MyAction()
{
    var customFilePath = GetCustomFilePath(); // may not be set
    if (string.IsNullOrWhiteSpace(customFilePath)) // fall back to default
        customFilePath = ConfigSettings.DefaultFilePath;
}

因此,您的控制器操作依赖于静态ConfigSettings类,但在这种情况下它不是可注入/可交换的依赖项。当单元测试运行器调用此方法,并且 customFilePath 为 null / empty / whitespace 时,它​​将进入 ConfigSettings get 方法,该方法将调用 ConfigurationManager.AppSettings,该方法将在单元测试项目的 app.config/appSettings 中查找值.

于 2013-03-24T13:36:48.677 回答