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

Looking through the github issue's comments, I found a work around that can go in the msbuild file...

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

This makes it easier to verify existing tests under .NET Core before porting the configuration data over to json configuration files.

Edit

If running under Resharper, the previous answer doesn't work as Resharper proxies the assembly, so you need

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

If you check the result of the call to ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

It should tell you where the required configuration file should be while running unit tests for that assembly.

I found that instead of having an app.config file, ConfigurationManager was looking for a testhost.dll.config file.

This was for a project targeting netcoreapp2.1, with a reference to Microsoft.NET.Test.Sdk,NUnit 3.11 and Nunit3TestAdapter 3.12.0


.CORE 3.1 To find out what dll.config file was being used, I debugged the test by adding this line and looking to see what the value is.

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

Then I found out resharper was using testhost.dll.config and VStest was using testhost.x86.dll.config. I needed to add the following lines to the project file.

  <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>

I came across the same issue with my xunit tests and solved it by using the instance of Configuration from ConfigurationManager. I put the static (normal) way it works in core, framework (but not unit tests) before I show the alternative way it works in all three:

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

And here is a similar/related issue. In case anyone needs to get a section you can do a similar thing, though the type must change in the app config:

<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>

Code to grab section:

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

I feel like I am also crazy, and I know there are newer ways of doing config in core, but if one wants to do something cross-platform this is the only way I know how. I'd be very interested if anyone has alternatives


For my mixed .NET-Core & .NET-Framework project, I added the following to the unit test global setup:

#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

This copies the config file to the output path testhost.dll.config but should be resilient enough to account for future changes in the testing framework.

Or you can copy to below, which amounts to the same thing:

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

Credit to @stop-cran and @PaulHatcher's solutions, this is a combination of those two.