2012-02-28 4 views
87

MOQ Я застрял в этой точке кода, который я не знаю, как издеваются:Как издеваются ConfigurationManager.AppSettings с

ConfigurationManager.AppSettings["User"]; 

Я должен дразнить ConfigurationManager, но я понятия не имею , Я использую Moq.

Кто-нибудь может дать мне совет? Благодаря!

ответ

84

Я считаю, что один стандартный подход к этому заключается в использовании шаблона , чтобы обернуть диспетчер конфигурации, а затем у вас есть что-то слабо связанное, что у вас есть контроль над.

Таким образом, вы можете обернуть ConfigurationManager. Что-то вроде:

public class Configuration: IConfiguration 
{ 
    public User 
    { 
     get{ 
       return ConfigurationManager.AppSettings["User"]; 
     } 
    } 
} 

(Вы можете просто извлечь интерфейс из вашего класса конфигурации, а затем использовать этот интерфейс везде в вашем коде) Тогда вы просто дразнят IConfiguration. Возможно, вы сможете реализовать сам фасад несколькими способами. Выше я решил просто обернуть отдельные свойства. Вы также получаете преимущество использования строго типизированной информации для работы с, а не слабо типизированными массивами хэшей.

+6

Это концептуально то, что я тоже делаю. Тем не менее, я использую Castle DictionaryAdapter (часть [Castle] (http://www.castleproject.org) Core), которая генерирует реализацию интерфейса «на лету». Я писал об этом некоторое время назад: http://blog.andreloker.de/post/2008/09/05/Getting-rid-of-strings-(3)-take-your-app-settings-to- the-next-level.aspx (прокрутите вниз до «A Solution», чтобы увидеть, как я использую Castle DictionaryAdapter) –

+0

Это отличная статья, и это хорошая статья. Я должен буду помнить об этом в будущем. –

+0

Я мог бы добавить - в зависимости от вашего пуризма и интерпретаций - это может быть вместо этого или также называться прокси-сервером делегата или адаптером. –

16

Возможно, это не то, что вам нужно выполнить, но считаете ли вы, что используете приложение app.config в своем тестовом проекте? Таким образом, ConfigurationManager получит значения, которые вы добавили в app.config, и вам не нужно ничего издеваться. Это решение работает хорошо для моих нужд, потому что мне никогда не нужно тестировать «переменный» файл конфигурации.

+7

Если поведение тестируемого кода изменяется в зависимости от значения значения конфигурации, это, безусловно, проще проверить его, если он напрямую не зависит от AppSettings. –

+0

Это плохая практика, поскольку вы никогда не проверяете другие возможные настройки. Ответ Джошуа Энфилда отлично подходит для тестирования. – mkaj

+3

Хотя другие против этого ответа, я бы сказал, что их позиция немного обобщена. Это очень правильный ответ в некоторых сценариях, и это действительно зависит от того, что вам нужно. Например, допустим, у меня есть 4 разных кластера, каждый из которых имеет другой базовый URL. Эти 4 кластера вытягиваются во время выполнения из 'Web.config', охватывающего проект. Во время тестирования вытягивание некоторых известных значений из 'app.config' очень актуально. Единичный тест просто должен убедиться, что условия, когда он тянет, говорят, что «cluster1» работает; в этом случае есть только 4 разных кластера. –

2

Это статическое свойство, а Moq предназначено для методов или классов экземпляра Moq, которые можно издеваться через наследование. Другими словами, Moq не поможет вам здесь.

Для издевательской статики я пользуюсь инструментом Moles, который является бесплатным. Существуют и другие средства изоляции рамки, такие как Typemock, которые тоже могут это сделать, хотя я считаю, что это платные инструменты.

Когда речь заходит о статике и тестировании, другой вариант заключается в том, чтобы создать статическое состояние самостоятельно, хотя это часто может быть проблематичным (как, я думаю, это будет в вашем случае).

И, наконец, если рамки изоляции не являются опцией, и вы привержены этому подходу, фасад, упомянутый Джошуа, - это хороший подход или любой подход в целом, когда вы кодируете код клиента из бизнес-логику, которую вы используете для тестирования.

13

Вы можете использовать прокладки, чтобы изменить AppSettings на заказ NameValueCollection. Вот пример того, как можно достичь этого:

[TestMethod] 
public void TestSomething() 
{ 
    using(ShimsContext.Create()) { 
     const string key = "key"; 
     const string value = "value"; 
     ShimConfigurationManager.AppSettingsGet =() => 
     { 
      NameValueCollection nameValueCollection = new NameValueCollection(); 
      nameValueCollection.Add(key, value); 
      return nameValueCollection; 
     }; 

     /// 
     // Test code here. 
     /// 

     // Validation code goes here.   
    } 
} 

Вы можете прочитать больше о прокладках и подделках на, Isolating Code Under Test with Microsoft Fakes. Надеюсь это поможет.

+5

Автор спрашивает, как с moq, а не с MS Fakes. – JPCF

+6

И как это отличается? Он достигает насмешек, удаляя зависимость данных от своего кода. Использование C# Fakes - это один из подходов! – Zorayr

87

Я использую AspnetMvc4. Момент назад я написал

ConfigurationManager.AppSettings["mykey"] = "myvalue"; 

в моем методе тестирования, и он отлично работал.

Объяснение: метод тестирования работает в контексте с настройками приложения, взятыми из, как правило, web.config или myapp.config.ConfigurationsManager может достигнуть этого глобального объекта-приложения и управлять им.

Хотя: Если у вас параллельно работает тестовый бегун, это не очень хорошая идея.

+3

Это действительно умный и простой способ решить проблему!Престижность для простоты! – Navap

+1

Намного легче, чем создавать абстракцию в большинстве случаев –

+0

Это все ???? Блеск в простоте, так как я ломал голову над тем, как проверить этот конкретный закрытый класс. – ppumkin

7

Вы считали, что вместо того, чтобы насмехаться? Свойство AppSettings является NameValueCollection:

[TestClass] 
public class UnitTest1 
{ 
    [TestMethod] 
    public void TestMethod1() 
    { 
     // Arrange 
     var settings = new NameValueCollection {{"User", "Otuyh"}}; 
     var classUnderTest = new ClassUnderTest(settings); 

     // Act 
     classUnderTest.MethodUnderTest(); 

     // Assert something... 
    } 
} 

public class ClassUnderTest 
{ 
    private readonly NameValueCollection _settings; 

    public ClassUnderTest(NameValueCollection settings) 
    { 
     _settings = settings; 
    } 

    public void MethodUnderTest() 
    { 
     // get the User from Settings 
     string user = _settings["User"]; 

     // log 
     Trace.TraceInformation("User = \"{0}\"", user); 

     // do something else... 
    } 
} 

Преимущества более простой реализации и не зависимость от System.Configuration, пока вы действительно не нужно.

+0

Мне нравится этот подход лучший. С одной стороны, перенос менеджера конфигурации с помощью «IConfiguration», поскольку предложение Джошуа Энфилда может быть слишком высоким, и вы можете пропустить ошибки, которые существуют из-за таких вещей, как синтаксический анализ плохих параметров. С другой стороны, использование 'ConfigurationManager.AppSettings' непосредственно в качестве LosManos предполагает слишком большую детализацию реализации, не говоря уже о том, что он может иметь побочные эффекты для других тестов и не может использоваться в параллельных тестовых запусках без ручной синхронизации (как« NameValueConnection » не является потокобезопасным). –

1

Я думаю, что написать собственный поставщик app.config - простая задача и более полезная, чем что-либо еще. Особенно вам следует избегать любых подделок, таких как прокладки и т. Д., Потому что, как только вы их используете Редактировать & Продолжить работу.

Поставщики я использую выглядеть следующим образом:

По умолчанию они получают значения из App.config, но и для модульных тестов я могу переопределить все значения и использовать их в каждом испытании самостоятельно.

Нет необходимости в каких-либо интерфейсах или реализовывать его каждый раз снова и снова. У меня есть утилита dll и использую этот небольшой помощник во многих проектах и ​​модульных тестах.

public class AppConfigProvider 
{ 
    public AppConfigProvider() 
    { 
     ConnectionStrings = new ConnectionStringsProvider(); 
     AppSettings = new AppSettingsProvider(); 
    } 

    public ConnectionStringsProvider ConnectionStrings { get; private set; } 

    public AppSettingsProvider AppSettings { get; private set; } 
} 

public class ConnectionStringsProvider 
{ 
    private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); 

    public string this[string key] 
    { 
     get 
     { 
      string customValue; 
      if (_customValues.TryGetValue(key, out customValue)) 
      { 
       return customValue; 
      } 

      var connectionStringSettings = ConfigurationManager.ConnectionStrings[key]; 
      return connectionStringSettings == null ? null : connectionStringSettings.ConnectionString; 
     } 
    } 

    public Dictionary<string, string> CustomValues { get { return _customValues; } } 
} 

public class AppSettingsProvider 
{ 
    private readonly Dictionary<string, string> _customValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); 

    public string this[string key] 
    { 
     get 
     { 
      string customValue; 
      return _customValues.TryGetValue(key, out customValue) ? customValue : ConfigurationManager.AppSettings[key]; 
     } 
    } 

    public Dictionary<string, string> CustomValues { get { return _customValues; } } 
} 
Смежные вопросы