[英]app.config not beeing loaded in .Net Core MSTests project
I'm working on a project were I'm trying to port several libraries from .NET Framework 4.5.2 to .NET Core 2, and I'm facing some problems trying to read legacy app.config appsettings in unit tests.我正在做一个项目,我正在尝试将几个库从 .NET Framework 4.5.2 移植到 .NET Core 2,并且我在尝试读取单元测试中的遗留 app.config appsettings 时遇到了一些问题。 To reduce the problem to a bare minimum reproduction scenario I've created the following project in VS2017:
为了将问题减少到最低限度的再现场景,我在 VS2017 中创建了以下项目:
I have the app.config file:我有 app.config 文件:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="TestKey" value="20" />
</appSettings>
<configSections>
</configSections>
</configuration>
And the UnitTest1.cs file:和 UnitTest1.cs 文件:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Configuration;
namespace SimpleTestsUnits
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void FromConfigurationManager()
{
Assert.AreEqual("20", ConfigurationManager.AppSettings["TestKey"]);
}
}
}
And upon building this project the SimpleTestsUnits.dll is generated and the SimpleTestsUnits.dll.config is created with the content of the app.config file in the same folder of the SimpleTestsUnits.dll file.在构建此项目时,将生成 SimpleTestsUnits.dll,并使用与 SimpleTestsUnits.dll 文件相同文件夹中的 app.config 文件的内容创建 SimpleTestsUnits.dll.config。
So, when I run the unit test using VS2017 the value of "TestKey" is always null and if I debug into the ConfigurationManager.AppSettings there is no key loaded there.因此,当我使用 VS2017 运行单元测试时,“TestKey”的值始终为 null,如果我调试到 ConfigurationManager.AppSettings,则那里没有加载密钥。
Exception thrown: 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException' in Microsoft.VisualStudio.TestPlatform.TestFramework.dll An exception of type 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException' occurred in Microsoft.VisualStudio.TestPlatform.TestFramework.dll but was not handled in user code Assert.AreEqual failed.
抛出的异常:Microsoft.VisualStudio.TestPlatform.TestFramework.dll 中的 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException'但未在用户代码中处理 Assert.AreEqual 失败。 Expected:<20>.
预期:<20>。 Actual:<(null)>.
实际:<(空)>。
What am I missing here?我在这里错过了什么? Shouldn't this be working?
这不应该工作吗?
MSTest is running as "testhost.dll", which means that ConfigurationManager is reading settings from "testhost.dll.config" when executing under .NET core. MSTest 作为“testhost.dll”运行,这意味着 ConfigurationManager 在 .NET core 下执行时正在从“testhost.dll.config”读取设置。 It will look for "testhost.dll.config" where the "testhost.dll" is located but it will also also look for "testhost.dll.config" in the location where you have your test dlls.
它会在“testhost.dll”所在的位置查找“testhost.dll.config”,但它还会在您拥有测试 dll 的位置查找“testhost.dll.config”。
So copying or renaming your config file in explorer to "testhost.dll.config" will solve the problem.因此,将资源管理器中的配置文件复制或重命名为“testhost.dll.config”将解决问题。
You can easily automate this by adding the following MSBuild step to the end of the MSTest .csproj file, within the "Project" tag.您可以通过将以下 MSBuild 步骤添加到 MSTest .csproj 文件末尾的“项目”标记中轻松实现此操作。
<Target Name="CopyAppConfig" AfterTargets="Build" DependsOnTargets="Build">
<CreateItem Include="$(OutputPath)$(AssemblyName).dll.config">
<Output TaskParameter="Include" ItemName="FilesToCopy"/>
</CreateItem>
<Copy SourceFiles="@(FilesToCopy)" DestinationFiles="$(OutputPath)testhost.dll.config" />
</Target>
Source: ( https://github.com/Microsoft/testfx/issues/348#issuecomment-454347131 )来源:( https://github.com/Microsoft/testfx/issues/348#issuecomment-454347131 )
When you execute the tests, the entry assembly is not an assembly with your tests.当您执行测试时,入口程序集不是包含您的测试的程序集。 You can check it by adding following line to your test and debugging it:
您可以通过在测试中添加以下行并调试来检查它:
var configLocation = Assembly.GetEntryAssembly().Location;
In my case configLocation
was c:\\Users\\myusername\\.nuget\\packages\\microsoft.testplatform.testhost\\15.3.0-preview-20170628-02\\lib\\netstandard1.5\\testhost.dll
在我的情况下
configLocation
是c:\\Users\\myusername\\.nuget\\packages\\microsoft.testplatform.testhost\\15.3.0-preview-20170628-02\\lib\\netstandard1.5\\testhost.dll
So ConfigurationManager
expects to find app.config
at testhost.dll.config
in specified directory.所以
ConfigurationManager
期望在指定目录中的testhost.dll.config
中找到app.config
。 I've copied it to this location and the test passed ok (after slight modification of the config, see below).我已经将它复制到这个位置并且测试通过了(在稍微修改配置之后,见下文)。
Another problem is that your app.config is not fully correct.另一个问题是您的 app.config 不完全正确。
configSections
element should be the first in <configuration>
root. configSections
元素应该是<configuration>
根中的第一个元素。 So just remove configSections
element as it's empty or adjust your app.config
in the following way:因此,只需删除
configSections
元素,因为它是空的,或者按以下方式调整您的app.config
:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
</configSections>
<appSettings>
<add key="TestKey" value="20" />
</appSettings>
</configuration>
Of course, it's a bad way to place config file near testhost.dll
.当然,将配置文件
testhost.dll
附近是一种不好的方式。 You could change the path from which ConfigurationManager
loads application config with ConfigurationManager.OpenExeConfiguration
call:您可以使用
ConfigurationManager.OpenExeConfiguration
调用更改ConfigurationManager
加载应用程序配置的路径:
[TestMethod]
public void UnitTest1()
{
// Put your Test assembly name here
Configuration configuration = ConfigurationManager.OpenExeConfiguration(@"SimpleTestsUnits.dll");
Assert.AreEqual("20", configuration.AppSettings.Settings["TestKey"].Value);
}
But unfortunately this approach requires modification of your code under test.但不幸的是,这种方法需要修改您的被测代码。
You can try this class for size.你可以试试这个类的大小。
public static class AppConfig
{
const string Filename = "app.config";
static readonly Configuration Configuration;
static AppConfig()
{
try
{
Configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
if (!Configuration.HasFile)
throw new ConfigurationErrorsException();
}
catch
{
try
{
var configmap = new ExeConfigurationFileMap
{
ExeConfigFilename = Filename
};
Configuration = ConfigurationManager.OpenMappedExeConfiguration(configmap, ConfigurationUserLevel.None);
}
catch
{
}
}
}
public static string Get(string key, string @default = "")
{
return Configuration?.AppSettings?.Settings[key]?.Value ?? @default;
}
}
I implemented the solution that Oskar Sjöberg presented.我实施了Oskar Sjöberg提出的解决方案。 It worked well for our solutions until we began to use multi-targeting.
在我们开始使用多目标之前,它适用于我们的解决方案。
The MSBuild step that Oskar presented would attempt to copy files from a location that was not being used as an output directory for building. Oskar 提供的 MSBuild 步骤将尝试从未用作 output 构建目录的位置复制文件。 It would produce an error as it could not find the
app.config
files that it was looking for.它会产生一个错误,因为它找不到它正在寻找的
app.config
文件。
I ended up adding Condition="'$(OutDir)' != ''"
to the target definition.我最终将
Condition="'$(OutDir)' != ''"
添加到目标定义中。
Note: I have not tested this across many platforms or many project styles. It may be the case that this prevents the copying from taking place.注意:我没有在许多平台或许多项目 styles 上对此进行测试。这可能会阻止复制发生。
It then looked like this:然后看起来像这样:
<Target Name="CopyAppConfig" AfterTargets="Build" DependsOnTargets="Build" Condition="'$(OutDir)' != ''">
<CreateItem Include="$(OutputPath)$(AssemblyName).dll.config">
<Output TaskParameter="Include" ItemName="FilesToCopy" />
</CreateItem>
<Copy SourceFiles="@(FilesToCopy)" DestinationFiles="$(OutputPath)testhost.dll.config" />
</Target>
BTW I wanted to comment on that post but I do not have enough reputation yet.顺便说一句,我想对该帖子发表评论,但我还没有足够的声誉。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.