1. 程式人生 > >.NetCore技術研究-ConfigurationManager在單元測試下的坑

.NetCore技術研究-ConfigurationManager在單元測試下的坑

最近在將原有程式碼遷移.NET Core, 程式碼的遷移基本很快,當然也遇到了不少坑,重構了不少,後續逐步總結分享給大家。今天總結分享一下ConfigurationManager遇到的一個問題。

先說一下場景:

   遷移.NET Core後,已有的配置檔案,我們希望做到相容,比如說app.config和web.config,

   這樣配置檔案儘可能地和.NET Framework是一套,儘可能低保持一致。比如:appSettings、自定義configSection等等。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="CustomConfigs" type="ClassLibraryNetStandard.CustomConfigHandler, ClassLibraryNetStandard"/>
  </configSections>
  <CustomConfigs>
    <CustomConfig name="service1" order="0" reflectconfig="ClassLibraryNetStandard.TestService, ClassLibraryNetStandard"/>
    <CustomConfig name="service2" order="1" reflectconfig="ClassLibraryNetStandard.TestService2, ClassLibraryNetStandard"/>
  </CustomConfigs>  
  <appSettings>
    <add key="service" value="service1"/>
  </appSettings>
</configuration>

 對於上面配置讀取我們做了以下幾個事情

   1. 新增Nuget:System.Configuration.ConfigurationManager

   2. 保證原有自定義Section配置相關的程式碼、讀取配置的程式碼,遷移到.NET Core後編譯通過

   3. 修改配置檔案、單元測試

 一、新增Nuget:System.Configuration.ConfigurationManager

   搜尋System.Configuration.ConfigurationManager:找到Nuget包,並新增引用:

   

二、保證原有自定義Section配置相關的程式碼、讀取配置的程式碼,遷移到.NET Core後編譯通過

  示例程式碼中,自定義配置類CustomConfig:

using System;
using System.Collections.Generic;
using System.Text;

namespace ClassLibraryNetStandard
{
    public class CustomConfig
    {
        public string Name { get; set; }

        public string ReflectConfig { get; set; }

        public int Order { get; set; }
    }
}

  同時對應的Section配置節解析類:CustomConfigHandler,實現介面:System.Configuration.IConfigurationSectionHandler

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace ClassLibraryNetStandard
{
   public class CustomConfigHandler : System.Configuration.IConfigurationSectionHandler
    {
        public object Create(object parent, object configContext, XmlNode section)
        {
            var configs = new List<CustomConfig>();

            //獲取配置檔案中自定義節點值  
            foreach (XmlNode childNode in section.ChildNodes)
            {
                string name = null;
                var config = new CustomConfig();
                if (childNode.Attributes["name"] != null)
                {
                    name = childNode.Attributes["name"].Value;
                    config.Name = name;

                    if (childNode.Attributes["order"] != null)
                    {
                        config.Order = Convert.ToInt32(childNode.Attributes["order"].Value);
                    }
                    if (childNode.Attributes["reflectconfig"] != null)
                    {
                        config.ReflectConfig = childNode.Attributes["reflectconfig"].Value;
                    }                  

                    configs.Add(config);
                }
            }

            return configs;
        }
    }
}

    同時,我們編寫了一個簡單的配置管理類:CustomConfigManager,其中有配置讀取方法,直接讀取配置檔案:

        public static List<CustomConfig> GetCustomConfigs()
        {
            var sectionConfig = System.Configuration.ConfigurationManager.GetSection("CustomConfigs");
            if (sectionConfig != null)
            {
                return  sectionConfig as List<CustomConfig>;
            }

            return null;
        }

  

  這裡我們使用了.NET Standard 2.0 library project,程式碼編譯通過:

1>------ 已啟動全部重新生成: 專案: ClassLibraryNetStandard, 配置: Debug Any CPU ------
1>C:\Program Files\dotnet\sdk\3.0.100-preview3-010431\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.RuntimeIdentifierInference.targets(151,5): message NETSDK1057: 你正在使用 .NET Core 的預覽版。請檢視 https://aka.ms/dotnet-core-preview
1>ClassLibraryNetStandard -> C:\Users\***\source\repos\NETFrameworkTest\ClassLibraryNetStandard\bin\Debug\netstandard2.0\ClassLibraryNetStandard.dll
========== 全部重新生成: 成功 1 個,失敗 0 個,跳過 0 個 ==========

   三、修改配置檔案、單元測試

  新增MSTest單元測試工程:   

  

   增加App.config配置檔案:

   

   在單元測試方法中測試配置的讀取:

       [TestMethod]
        public void ConfigTest()
        {
            var configs = ClassLibraryNetStandard.CustomConfigManager.GetCustomConfigs();
            Assert.IsNotNull(configs);
        }

  原本以為,肯定可以獲取到配置,實際獲取的configs是null。

        換了個Console類的應用,同樣的配置檔案讀取,一點沒有問題:

      

      對比看了一下這兩個工程,發現除了實際編譯生成的配置檔名稱不同,其他都一樣。

      問題肯定出在了單元測試工程上。Google了一下:有以下發現:       

MSTest is running as testhost.dll, which means that ConfigurationManager is reading settings from testhost.dll.config when executing under .NET core. 
It will look for testhost.dll.config where the testhost.dll is located as the accepted answer states.
What is not mentioned is that it will also look for testhost.dll.config in the location where you have your test dlls.

  一句話:MSTest以testhost.dll執行,去取的配置檔案是testhost.dll.config

        這太尷尬了,直接無語,不過有兩個解決方案:

        1. 直接在單元測試工程中將app.config檔案改為:testhost.dll.config

        2. 修改單元測試工程檔案,配置編譯後事件,動態copy生成testhost.dll.config

       

      試過之後,果真可以了,問題解決,分享個大家。

 

 

周國慶

2019/