ASP.NET MVC放置常量的最佳位置(从TDD角度来看)

时间:2013-03-24 12:25:05

标签: asp.net-mvc unit-testing tdd constants xunit

创建用户个人资料的控制器正在使用个人资料视图模型,该模型的一部分是默认徽标(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(看起来很难看) - 而且由于Config类是静态的并且无法实现接口,所以它不起作用
  • 我没有想到的事情,因此要求出色的想法。

非常感谢

1 个答案:

答案 0 :(得分: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文件中放置一个app.config节点,非常类似于如何在您的网络项目的appSettings文件中拥有web.config节点。单元测试运行器将从app.config中提取值,Web服务器将从web.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?我应该通过吗?   这个静态类作为构造函数的参数?

不,你不需要做任何特别的事情。只需将它用作类,就像使用静态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 for the value。