ASP.NET 系列:單元測試之ConfigurationManager

經過ConfigurationManager使用.NET配置文件時,能夠經過添加配置文件進行單元測試,雖然能夠經過測試但達不到解耦的目的。使用IConfigurationManager和ConfigurationManagerWrapper對ConfigurationManager進行適配是更好的方式,ConfigurationManagerWrapper提供.NET配置文件方式的實現,若是須要支持其餘配置,建立IConfigurationManager接口的不一樣的實現類便可。node

1.定義IConfigurationManager接口

本來依賴ConfigurationManager的代碼如今依賴IConfigurationManager。能夠在單元測試時方便的Mock。app

public interface IConfigurationManager
{
    NameValueCollection AppSettings { get; }
    ConnectionStringSettingsCollection ConnectionStrings { get; }
    object GetSection(string sectionName);
}

2.建立適配類ConfigurationManagerWrapper

非單元測試環境使用ConfigurationManagerWrapper做爲IConfigurationManager的默認實現。框架

public class ConfigurationManagerWrapper : IConfigurationManager
{
    public NameValueCollection AppSettings
    {
        get
        {
            return ConfigurationManager.AppSettings;
        }
    }

    public ConnectionStringSettingsCollection ConnectionStrings
    {
        get
        {
            return ConfigurationManager.ConnectionStrings;
        }
    }

    public object GetSection(string sectionName)
    {
        return ConfigurationManager.GetSection(sectionName);
    }
}

3.自定義泛型配置接口

在咱們的代碼須要使用配置時,能夠考慮建立通用的泛型接口也能夠使用專用的強類型的接口。這裏演示使用通用的接口。單元測試

public interface IConfiguration
{
    T Get<T>(string key, T @default);
}

4.實現泛型接口配置接口的.NET配置文件版本

AppConfigAdapter直接不使用ConfigurationManager而是依賴IConfigurationManager接口。測試

public class AppConfigAdapter : IConfiguration
{
    private IConfigurationManager _configurationManager;

    public AppConfigAdapter(IConfigurationManager configurationManager)
    {
        this._configurationManager = configurationManager;
    }

    public T Get<T>(string nodeName, T @default)
    {
        var value = this._configurationManager.AppSettings[nodeName];
        return value == null ? @default : (T)Convert.ChangeType(value, typeof(T));
    }
}

5.對泛型配置接口的實現進行單元測試

使用最流行的單元測試框架和Mock類庫:xUnit+Moq進行單元測試。this

public class AppConfigAdapterTest
{
    [Fact]
    public void GetStringTest()
    {
        var key = "key";
        var value = "value";
        var configuration = new AppConfigAdapter(this.GetConfigurationManager(o => o.Add(key, value.ToString())));
        Assert.Equal(configuration.Get(key, string.Empty), value);
    }

    [Fact]
    public void GetIntTest()
    {
        var key = "key";
        var value = 1;
        var configuration = new AppConfigAdapter(this.GetConfigurationManager(o => o.Add(key, value.ToString())));
        Assert.Equal(configuration.Get(key, int.MinValue), value);
    }

    [Fact]
    public void GetBoolTest()
    {
        var key = "key";
        var value = true;
        var configuration = new AppConfigAdapter(this.GetConfigurationManager(o => o.Add(key, value.ToString())));
        Assert.Equal(configuration.Get(key, false), value);
    }

    [Fact]
    public void GetDateTimeTest()
    {
        var key = "key";
        var value = DateTime.Parse(DateTime.Now.ToString());
        var configuration = new AppConfigAdapter(this.GetConfigurationManager(o => o.Add(key, value.ToString())));
        Assert.Equal(configuration.Get(key, DateTime.MinValue), value);
    }

    [Fact]
    public void GetDecimalTest()
    {
        var key = "key";
        var value = 1.1m;
        var configuration = new AppConfigAdapter(this.GetConfigurationManager(o => o.Add(key, value.ToString())));
        Assert.Equal(configuration.Get(key, decimal.MinValue), value);
    }

    private IConfigurationManager GetConfigurationManager(Action<NameValueCollection> set)
    {
        var appSettings = new NameValueCollection();
        set(appSettings);
        var configurationManager = new Mock<IConfigurationManager>();
        configurationManager.Setup(o => o.AppSettings).Returns(appSettings);
        return configurationManager.Object;
    }
}

運行結果:spa

6.總結

使依賴ConfigurationManager靜態類的代碼轉換爲依賴IConfigurationManager接口,運行時注入ConfigurationManagerWrapper實現類。單元測試時使用Mock模擬IConfigurationManager對象。code

相關文章
相關標籤/搜索