簡體   English   中英

"無法使用 ConfigurationManager 在 C# .NET Core 單元測試項目中讀取 app.config"

[英]Can't read app.config in C# .NET Core unit test project with ConfigurationManager

我創建了一個簡單的單元測試項目來讀取 app.config 文件。 目標框架是 Core 2.0。 我還創建了一個 Core 2.0 控制台應用程序,對自己進行健全性檢查,以確保我沒有做任何奇怪的事情(在 .NET 4.6.1 單元測試項目中通過了與預期相同的測試)。

控制台應用程序讀取 app.config 很好,但單元測試方法失敗,我不知道為什么。 兩者都使用相同 app.config 的副本(未作為鏈接添加)並且都安裝了 System.Configuration.ConfigurationManager v4.4.1 NuGet 包。

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="Test1" value ="This is test 1."/>
    <add key="Test2" value ="42"/>
    <add key="Test3" value ="-42"/>
    <add key="Test4" value="true"/>
    <add key="Test5" value="false"/>
    <add key="Test6" value ="101.101"/>
    <add key="Test7" value ="-1.2345"/>
  </appSettings>
</configuration>

單元測試

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Configuration;

namespace ConfigTest
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod()]
        public void ConfigTest()
        {
            foreach (string s in ConfigurationManager.AppSettings.AllKeys)
            {
                System.Console.WriteLine(s);
                System.Diagnostics.Debug.WriteLine(s);
            }

            //AllKeys.Length is 0? Should be 7...
            Assert.IsTrue(ConfigurationManager.AppSettings.AllKeys.Length == 7);
        }
    }
}

控制台應用程序

using System;
using System.Configuration;

namespace ConfigTestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (string s in ConfigurationManager.AppSettings.AllKeys)
            {
                Console.WriteLine(s);
                System.Diagnostics.Debug.WriteLine(s);
            }

            //Outputs 7 as expected
            Console.WriteLine(ConfigurationManager.AppSettings.AllKeys.Length);
        }
    }
}  

鑒於我對整個 .NET Core 世界還很陌生,我在這里做的事情是否完全不正確? 此刻我感覺有點瘋狂...

兩個項目都帶有 app.config

查看github問題的評論,我發現了一個可以進入msbuild文件的解決方法......

<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
  <Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.dll.config" />
</Target>

這使得在將配置數據移植到 json 配置文件之前更容易驗證 .NET Core 下的現有測試。

編輯

如果在 Resharper 下運行,則先前的答案在 Resharper 代理程序集時不起作用,因此您需要

<Target Name="CopyCustomContent" AfterTargets="AfterBuild">
  <Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\ReSharperTestRunner64.dll.config" />
</Target>

如果您檢查調用ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);的結果ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

它應該告訴您在為該程序集運行單元測試時所需的配置文件應該在哪里。

我發現 ConfigurationManager 正在尋找testhost.dll.config文件,而不是app.config文件。

這是針對netcoreapp2.1的項目,參考了Microsoft.NET.Test.SdkNUnit 3.11Nunit3TestAdapter 3.12.0

.CORE 3.1 為了找出正在使用的 dll.config 文件,我通過添加這一行並查看值是什么來調試測試。

string path = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;

然后我發現 resharper 使用的是 testhost.dll.config 而 VStest 使用的是 testhost.x86.dll.config。 我需要將以下行添加到項目文件中。

  <Target Name="CopyCustomContent" AfterTargets="AfterBuild">
    <Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.dll.config" />
    <Copy SourceFiles="app.config" DestinationFiles="$(OutDir)\testhost.x86.dll.config" />
  </Target>

我在 xunit 測試中遇到了同樣的問題,並通過使用 ConfigurationManager 中的 Configuration 實例解決了它。 在我展示它在所有三個中的替代方式之前,我將它的靜態(正常)方式放在核心、框架(但不是單元測試)中:

        var appSettingValFromStatic = ConfigurationManager.AppSettings["mySetting"];
        var appSettingValFromInstance = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).AppSettings.Settings["mySetting"].Value;

這是一個類似/相關的問題。 如果有人需要獲取一個部分,您可以做類似的事情,但類型必須在應用程序配置中更改:

<configSections>
    <section name="customAppSettingsSection" type="System.Configuration.AppSettingsSection"/>
    <section name="customNameValueSectionHandlerSection" type="System.Configuration.NameValueSectionHandler"/>
</configSections>

<customAppSettingsSection>
    <add key="customKey" value="customValue" />
</customAppSettingsSection>

<customNameValueSectionHandlerSection>
    <add key="customKey" value="customValue" />
</customNameValueSectionHandlerSection>

代碼抓取部分:

        var valFromStatic = ((NameValueCollection)ConfigurationManager.GetSection("customNameValueSectionHandlerSection"))["customKey"];
        var valFromInstance = ((AppSettingsSection)ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location).GetSection("customAppSettingsSection")).Settings["customKey"].Value;

我覺得我也很瘋狂,我知道在核心中有更新的配置方法,但是如果有人想做跨平台的事情,這是我知道的唯一方法。 如果有人有其他選擇,我會非常感興趣

當您處理直接訪問靜態ConfigurationManager屬性(例如AppSettingsConnectionStrings代碼時,此處給出的答案都沒有提供可行的解決方法。

事實是,目前是不可能的。 您可以通讀此處的討論以了解原因: https : //github.com/dotnet/corefx/issues/22101

這里有關於實現對它的支持的討論: https : //github.com/Microsoft/vstest/issues/1758

在我看來,支持這個場景是有意義的,因為它一直在 .NET Framework 上工作,而且System.Configuration.ConfigurationManager現在是一個 .NET Standard 2.0 庫。

對於我的混合 .NET-Core 和 .NET-Framework 項目,我將以下內容添加到單元測試全局設置中:

#if NETCOREAPP
using System.Configuration;
using System.IO;
using System.Reflection;
#endif

...

// In your global setup:
#if NETCOREAPP
    string configFile = $"{Assembly.GetExecutingAssembly().Location}.config";
    string outputConfigFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
    File.Copy(configFile, outputConfigFile, true);
#endif

testhost.dll.config配置文件復制到輸出路徑testhost.dll.config但應該具有足夠的彈性以應對測試框架中的未來更改。

或者你可以復制到下面,這相當於同樣的事情:

string outputConfigFile = Path.Combine(Path.GetDirectoryName(configFile), $"{Path.GetFileName(Assembly.GetEntryAssembly().Location)}.config");

感謝@stop-cran 和@PaulHatcher 的解決方案,這是兩者的結合。

ConfigurationManager API 將僅使用當前正在運行的應用程序的配置。 在單元測試項目中,這意味着測試項目的 app.config,而不是控制台應用程序。

.NET Core 應用程序不應使用 app.config 或ConfigurationManager ,因為它是傳統的“完整框架”配置系統。

考慮使用Microsoft.Extensions.Configuration來讀取 JSON、XML 或 INI 配置文件。 請參閱此文檔: https : //docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration

一個hacky但有效的方法是將配置復制到與條目程序集相同的文件夾中,無論它是什么:

[SetUpFixture]
public class ConfigKludge
{
    [OneTimeSetUp]
    public void Setup() =>
        File.Copy(
            Assembly.GetExecutingAssembly().Location + ".config",
            Assembly.GetEntryAssembly().Location + ".config",
            true);

    [OneTimeTearDown]
    public void Teardown() =>
        File.Delete(Assembly.GetEntryAssembly().Location + ".config");
}

除了添加這個類之外,唯一讓它工作的方法是在測試項目中包含app.config文件(沒有任何復制選項)。 它應該在構建步驟中作為<your test project name>.dll.config復制到輸出文件夾,因為它是一種默認邏輯。

請注意OneTimeSetUpAttribute的文檔:

摘要:標識在運行任何子測試之前調用一次以執行設置的方法。

雖然它應該適用於單個項目的並行測試運行,但同時運行兩個測試項目時可能會出現明顯的問題,因為配置會被覆蓋。

但是,它仍然適用於容器化的測試運行,例如在Travis 中

當我們回答這樣一個經過充分研究和明確表達的問題時,我們最好假設它是由一個知情和聰明的人提出的。 而不是光顧他們明顯的新的、偉大的方式編寫大量樣板代碼來解析各種 JSON 等,強加於我們並被知識淵博的人推到我們的喉嚨,我們應該專注於回答問題.

由於 OP 已經在使用System.Configuration來訪問設置,因此他們已經知道如何到達這一點。 唯一缺少的是一點點觸摸:將此行添加到構建后事件:

copy $(OutDir)<appname>.dll.config $(OutDir)testhost.dll.config

其中 <appname> 是被單元測試的項目。

我為仍在使用app.config實現(最初是蹩腳但可行的)的每個人鼓掌,因為這樣做可以保護我們和我們的客戶對技術的投資,而不是重新發明輪子。 阿門。

通常在 .NET Framework 項目中,任何 App.config 文件都由 Visual Studio 復制到 bin 文件夾中,並帶有可執行文件的名稱 (myApp.exe.config),以便在運行時可以訪問它。 不再出現在 .NET Standard 或 Core Framework 中。 您必須手動復制並設置 bin/debug 或 release 文件夾中的文件。 之后,它可能會得到類似的東西:

                string AssemblyName = System.IO.Path.GetFileName(System.Reflection.Assembly.GetEntryAssembly().GetName().CodeBase);
            AppConfig = (System.Configuration.Configuration)System.Configuration.ConfigurationManager.OpenExeConfiguration(AssemblyName);

幸運的是,現在有一種方法可以在運行時設置預期配置文件的名稱。 您可以為當前應用程序域設置APP_CONFIG_FILE數據。

我創建了以下 SetUpFixture 來自動執行此操作:

[SetUpFixture]
public class SetUpFixture
{
    [OneTimeSetUp]
    public void OneTimeSetUp()
    {
        var testDllName = Assembly.GetAssembly(GetType())
                                  .GetName()
                                  .Name;
        var configName = testDllName + ".dll.config";
        AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", configName);
    }
}

相關的 GitHub 討論是:

雖然 app.config 存在於根項目文件夾中,但將以下字符串添加到Post-build 事件命令行

xcopy /Y $(ProjectDir)app.config $(ProjectDir)$(OutDir)testhost.dll.config*

添加配置文件

首先,在集成測試項目中添加一個 appconfig.json 文件

通過更新配置appconfig.json文件復制到輸出目錄

在此處輸入圖片說明

添加 NuGet 包

  • Microsoft.Extensions.Configuration.Json

在單元測試中使用配置

[TestClass]
public class IntegrationTests
{


    public IntegrationTests()
    {
        var config = new ConfigurationBuilder().AddJsonFile("appconfig.json").Build();

        _numberOfPumps = Convert.ToInt32(config["NumberOfPumps"]);

        _numberOfMessages = Convert.ToInt32(config["NumberOfMessages"]);

        _databaseUrl = config["DatabaseUrlAddress"];
    }
} 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM