无法使用ConfigurationManager在C#.NET Core单元测试项目中读取app.config

时间:2018-02-07 14:54:12

标签: c# unit-testing .net-core app-config

我创建了一个简单的单元测试项目来读取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世界还很陌生,我在做一些完全不正确的事情吗?我此刻感到很疯狂......

Both projects with an app.config

11 个答案:

答案 0 :(得分:12)

。核心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>

答案 1 :(得分:5)

我在xunit测试中遇到了相同的问题,并通过使用ConfigurationManager中的Configuration实例解决了该问题。我先将其静态(正常)方式应用于核心框架(而不是单元测试),然后再展示其在所有三种方式中均可使用的替代方式:

 * More importantly, Aux allows us to write code like this:
   *
   * {{{
   *   def myMethod[T, R]()(implicit eqGen: Generic.Aux[T,R], repEq: Eq[R]) = ???
   * }}}
   *
   * Here, we specify T, and we find a Generic.Aux[T,R] by implicit search. We then use R in the second argument.
   * Generic.Aux[T, R] is exactly equivalent to Generic[T] { type Repr = R }, but Scala doesn't allow us to write
   * it this way:
   *
   * {{{
   *   def myMethod[T, R]()(eqGen: Generic[T] { Repr = R }, reqEq: Eq[egGen.Repr]) = ???
   * }}}

这是一个类似/相关的问题。万一有人需要一节,您可以做类似的事情,尽管类型必须在应用程序配置中更改:

        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>

我觉得自己也很疯狂,并且我知道在内核中有更新的配置方法,但是如果有人想跨平台做某事,那是我唯一知道的方法。如果有人有其他选择,我会很感兴趣

答案 2 :(得分:5)

如果您查看调用ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);的结果

它应该告诉您在运行该程序集的单元测试时所需的配置文件应该在哪里。

我发现ConfigurationManager而不是拥有app.config文件,而是在寻找testhost.dll.config文件。

这是针对以netcoreapp2.1Microsoft.NET.Test.SdkNUnit 3.11为参考的Nunit3TestAdapter 3.12.0的项目

答案 3 :(得分:4)

对于我的混合.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,但应具有足够的弹性以应对测试框架中的将来更改。

或者您可以复制到下面,表示相同的内容:

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

使用@ stop-cran和@PaulHatcher的解决方案,这是两者的结合。

答案 4 :(得分:3)

ConfigurationManager API仅使用当前正在运行的应用的配置。在单元测试项目中,这意味着测试项目的app.config,而不是控制台应用程序。

.NET核心应用程序不应该使用app.config或ConfigurationManager,因为它是一个传统的“完整框架”配置系统。

请考虑使用Microsoft.Extensions.Configuration来读取JSON,XML或INI配置文件。请参阅此文档:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration

答案 5 :(得分:2)

在处理直接访问静态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库。

答案 6 :(得分:2)

当我们回答这样一个经过充分研究和明确表达的问题时,我们最好假设它是由一位知情且聪明的人提出的。而不是光顾他们明显的新的、伟大的方式编写大量样板代码来解析各种 JSON 等,强加于我们并被知识渊博的人推到我们的喉咙,我们应该专注于回答问题.

由于 OP 已经在使用 System.Configuration 访问设置,因此他们已经知道如何到达这一点。唯一缺少的是一点点:将此行添加到构建后事件:

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

其中 是进行单元测试的项目。

我为仍在使用(最初蹩脚但可行)app.config 实现的每个人鼓掌,因为这样做可以保护我们和我们客户的技术投资,而不是重新发明轮子。阿门。

答案 7 :(得分:0)

通常在.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);

答案 8 :(得分:0)

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

答案 9 :(得分:0)

浏览github问题的评论,我发现可以在msbuild文件中找到解决方法...

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

这使得在将配置数据移植到json配置文件之前,可以更轻松地在.NET Core下验证现有测试。

答案 10 :(得分:0)

添加配置文件

首先,将一个appconfig.json文件添加到集成测试项目中

配置要复制到输出的appconfig.json文件    通过更新

enter image description here

添加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"];
    }
} 
相关问题