ConfigurationManager.AppSettings Returns Null In Unit Test Project

27,518

Solution 1

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>

Solution 2

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/

Solution 3

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.

Solution 4

And then he screamed "NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO".

Cite: 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.

You don't do this. EVER!!!! Why? because you have now created a dependency. Instead, use dependency injection so the class can do its work without having to peak into the configuration file that belongs to the application.

Share:
27,518
Jason Boyd
Author by

Jason Boyd

Civil engineer turned software developer. I am a lead developer/application architect working primarily in C#. Currently working on my second four year degree - this one for computer science.

Updated on August 05, 2022

Comments

  • Jason Boyd
    Jason Boyd over 1 year

    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"];