Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConfigurationManager.AppSettings Returns Null In Unit Test Project

I have a C# unit test project with application settings in the app.config file. I am testing a class that exists in a different project. That class depends on both, ConfigurationManager.AppSettings and ConfigurationManager.ConnectionStrings.

The project that the class being tested resides in does not have an app.config file. I would have thought that because the class is being instantiated in the context of the unit test project that it would use the unit test project's app.config file. Indeed, that does seem to be the case for the connection string.

The class retrieves the connection string without any issues. However, when the class tries to retrieve any application settings the configuration manager always returns null. What is going on here?

Edit 1

I thought maybe it would be a good idea to try load some settings in the test project to see what happens. I tried to load the setting in the unit test immediately before calling the code that instantiates the class in the external project. Same result, nothing. I guess I can exclude the other project from the equation for the time being.

Here is an excerpt from my config file:

<configSections>
  <sectionGroup name="applicationSettings"
                type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
    <section name="MyNamespace.Properties.Settings"
             type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
             requirePermission="false" />
  </sectionGroup>
</configSections>

...

<applicationSettings>
  <MyNamespace.Properties.Settings>
    <setting name="Bing_Key"
             serializeAs="String">
      <value>...</value>
    </setting>
  </MyNamespace.Properties.Settings>
</applicationSettings>

and here is how I am attempting to load the setting:

string test = System.Configuration.ConfigurationManager.AppSettings["Bing_Key"];
like image 232
Jason Boyd Avatar asked Jun 19 '14 13:06

Jason Boyd


People also ask

How do I use ConfigurationManager AppSettings?

AppSettings is actually a property, so you need to use square brackets. Overall, here's what you need to do: SqlConnection con= new SqlConnection(ConfigurationManager. AppSettings["ConnectionString"]);

What is ConfigurationManager AppSettings?

it is a .net builtin mechanism to define some settings before the application starts, without recompiling. see msdn configurationmanager.

Does ConfigurationManager work in .NET core?

ConfigurationManager was added to support ASP.NET Core's new WebApplication model, used for simplifying the ASP.NET Core startup code.

What is the use of ConfigurationManager in C#?

The ConfigurationManager class enables you to access machine, application, and user configuration information. This class replaces the ConfigurationSettings class, which is deprecated. For web applications, use the WebConfigurationManager class.


3 Answers

Consider refactoring your code that accesses the config to use a wrapper. Then you can write mocks for the wrapper class and not have to deal with the importing of the configuration file for the test.

In a library that is common to both, have something like this:

public interface IConfigurationWrapper {

    string GetValue(string key);
    bool HasKey(string key);
}

Then, in your libraries that need to access config, inject an instance of this interface type into the class that needs to read config.

public class MyClassOne {
    
    private IConfigurationWrapper _configWrapper;

    public MyClassOne(IConfigurationWrapper wrapper) {
        _configWrapper = wrapper;
    } // end constructor

    public void MethodThatDependsOnConfiguration() {
        string configValue = "";
        if(_configWrapper.HasKey("MySetting")) {
            configValue = _configWrapper.GetValue("MySetting");
        }
    } // end method

} // end class MyClassOne

Then, in one of your libraries, create an implementation that depends on the config file.

public class AppConfigWrapper : IConfigurationWrapper {
    
    public string GetValue(string key) {
        return ConfigurationManager.AppSettings[key];
    }

    public bool HasKey(string key) {
       return ConfigurationManager.AppSettings.AllKeys.Select((string x) => x.ToUpperInvariant()).Contains(key.ToUpperInvariant());
    }
}

Then, in the code that calls your class.

//Some method container
MyClassOne dataClass = new MyClassOne(new AppConfigWrapper());

dataClass.MethodThatDependsOnConfiguration();

Then in your test, you are free from dependency bondage. :) You can either create a fake version that implements IConfigurationWrapper and pass it in for your test, where you hard-code the return values from the GetValue and HasKey functions, or if you're using a mocking library like Moq:

Mock<IConfigurationWrapper> fakeWrapper = new Mock<IConfigurationWrapper>();

fakeWrapper.Setup((x) => x.GetValue(It.IsAny<string>)).Returns("We just bypassed config.");

MyClassOne testObject = new MyClassOne(fakeWrapper.Object);
testObject.MethodThatDependsOnConfiguration();

Here is an article that covers the concept (albeit, for web forms, but the concepts are the same): http://www.schwammysays.net/how-to-unit-test-code-that-uses-appsettings-from-web-config/

like image 136
xDaevax Avatar answered Oct 17 '22 14:10

xDaevax


You mentioned settings in the project properties. See if you can access the setting this way:

string test = Properties.Settings.Default.Bing_Key;

You may need to get the executing assembly of where the project settings file is defined, but try this first.

EDIT

When using Visual Studio's project settings file, it adds stuff to your app.config and creates the app.config if it is not present. ConfigurationManager CAN'T touch these settings! You can only get to these specific generated project.settings file from using the above static method. If you want to use ConfigurationManager, you will need to hand write your app.config. Add your settings to it like so:

<appSettings>
  <add key="bing_api" value="whatever"/>
</appSettings>
like image 10
Bill Sambrone Avatar answered Oct 17 '22 15:10

Bill Sambrone


If you're using .NET Core your problem could be a known issue caused by the fact that the test process runs as testhost.dll (or testhost.x86.dll), which means the runtime config file is expected to be named "testhost.dll.config" (or "testhost.x86.dll.config"), instead of the app.config output (ex: "MyLibrary.Tests.dll.config").

To fix it, add the code below to your project file (.csproj, etc) inside of root node <Project>. During build, two copies of app.config will be put in the output directory and named "testhost.dll.config" and "testhost.x86.dll.config", which will get your app settings working again. (You only need 1 of these files but it's safer to include both.)

<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 recommend app.config only as a temporary solution. If you're like me you might have run into the problem while upgrading a .NET Framework project to .NET Core and needed a quick fix. But don't forget to look into the new, more elegant solutions provided by .NET Core for storing app settings.

like image 10
Keith Avatar answered Oct 17 '22 14:10

Keith