ConfigurationManager.AppSettings Возвращает Null In Unit Test Project

У меня есть проект С# unit test с настройками приложения в файле app.config. Я тестирую класс, который существует в другом проекте. Этот класс зависит от обоих: ConfigurationManager.AppSettings и ConfigurationManager.ConnectionStrings.

В проекте, в котором находится тестируемый класс, нет файла app.config. Я бы подумал, что, поскольку класс создается в контексте проекта unit test, он будет использовать файл app.config проекта unit test. Действительно, это похоже на строку соединения.

Класс возвращает строку соединения без каких-либо проблем. Однако, когда класс пытается получить какие-либо параметры приложения, менеджер конфигурации всегда возвращает null. Что здесь происходит?

Изменить 1

Я подумал, что было бы неплохо попробовать загрузить некоторые настройки в тестовом проекте, чтобы узнать, что произойдет. Я попытался загрузить параметр в unit test непосредственно перед вызовом кода, который создает экземпляр класса во внешнем проекте. Тот же результат, ничего. Наверное, я могу исключить другой проект из уравнения на данный момент.

Вот выдержка из моего файла конфигурации:

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

и вот как я пытаюсь загрузить настройку:

string test = System.Configuration.ConfigurationManager.AppSettings["Bing_Key"];

Ответ 1

Вы упомянули настройки в свойствах проекта. Посмотрите, можете ли вы получить доступ к настройке таким образом:

string test = Properties.Settings.Default.Bing_Key;

Возможно, вам понадобится получить исполняющую сборку, где определен файл настроек проекта, но сначала попробуйте это.

ИЗМЕНИТЬ

При использовании файла настроек проекта Visual Studio он добавляет материал в ваш app.config и создает app.config, если он отсутствует. ConfigurationManager НЕ МОЖЕТ касаться этих настроек! Вы можете получить доступ к этому конкретному сгенерированному файлу project.settings из использования вышеуказанного статического метода. Если вы хотите использовать ConfigurationManager, вам нужно будет написать файл app.config. Добавьте к нему свои настройки следующим образом:

<appSettings>
  <add key="bing_api" value="whatever"/>
</appSettings>

Ответ 2

Рассмотрим рефакторинг кода, который обращается к конфигу, чтобы использовать обертку. Затем вы можете писать mocks для класса-оболочки и не иметь дело с импортом файла конфигурации для теста.

В библиотеке, которая является общей для обоих, есть что-то вроде этого:

public interface IConfigurationWrapper {

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

Затем в ваших библиотеках, которым необходимо получить доступ к 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

Затем в одной из ваших библиотек создайте реализацию, которая зависит от файла конфигурации.

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());
    }
}

Затем в коде, вызывающем ваш класс.

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

dataClass.MethodThatDependsOnConfiguration();

Затем в вашем тесте вы свободны от зависимости.:) Вы можете либо создать фальшивую версию, которая реализует IConfigurationWrapper, и передать ее для вашего теста, где вы жестко кодируете возвращаемые значения из функций GetValue и HasKey, или если вы используете насмешливую библиотеку, такую ​​как 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();

Вот статья, которая охватывает концепцию (хотя для веб-форм, но понятия одинаковы): http://www.schwammysays.net/how-to-unit-test-code-that-uses-appsettings-from-web-config/