Ответы:
Я считаю, что один из стандартных подходов к этому - использовать шаблон фасада для обертывания диспетчера конфигурации, и тогда у вас будет что-то слабо связанное, над которым вы можете контролировать.
Таким образом, вы должны обернуть ConfigurationManager. Что-то вроде:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(Вы можете просто извлечь интерфейс из своего класса конфигурации, а затем использовать этот интерфейс повсюду в своем коде). Затем вы просто имитируете IConfiguration. Вы могли бы реализовать сам фасад несколькими разными способами. Выше я решил просто обернуть отдельные свойства. Вы также получаете дополнительное преимущество в виде строго типизированной информации для работы с массивами хешей со слабой типизацией.
var configurationMock = new Mock<IConfiguration>();
и для настройки:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Я использую AspnetMvc4. Мгновение назад я написал
ConfigurationManager.AppSettings["mykey"] = "myvalue";
в моем методе тестирования, и он работал отлично.
Объяснение: метод тестирования запускается в контексте с настройками приложения, обычно из web.config
или myapp.config
. ConfigurationsManager
может достичь этого глобального объекта приложения и манипулировать им.
Хотя: если у вас есть тестер, запускающий тесты параллельно, это не лучшая идея.
ConfigurationManager.AppSettings
не NameValueCollection
является потокобезопасным, поэтому параллельные тесты с его использованием без надлежащей синхронизации в любом случае не являются хорошей идеей. В противном случае вы можете просто позвонить ConfigurationManager.AppSettings.Clear()
в свой TestInitialize
/ ctor, и вы золотой.
Возможно, это не то, что вам нужно сделать, но рассматривали ли вы возможность использовать app.config в своем тестовом проекте? Таким образом, ConfigurationManager получит значения, которые вы указали в app.config, и вам не нужно ничего имитировать. Это решение хорошо подходит для моих нужд, потому что мне никогда не нужно тестировать "переменный" файл конфигурации.
Web.config
окружающего проекта. Во время тестирования app.config
вполне допустимо извлечение некоторых хорошо известных значений из . Модульному тесту просто необходимо убедиться, что условия при его загрузке, скажем, "cluster1" работают; в этом случае есть только 4 разных кластера.
Вы можете использовать прокладки для изменения 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.
}
}
Вы можете узнать больше о прокладках и подделках в разделе «Изоляция тестируемого кода с помощью Microsoft Fakes» . Надеюсь это поможет.
Вы не думали о том, чтобы издеваться над заглушкой? 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 до тех пор, пока он вам действительно не понадобится.
IConfiguration
как предлагает Джошуа Энфилд, может быть слишком высоким уровнем, и вы можете пропустить ошибки, которые существуют из-за таких вещей, как неправильный синтаксический анализ значения конфигурации. С другой стороны, использование ConfigurationManager.AppSettings
напрямую, как предлагает LosManos, - это слишком большая часть деталей реализации, не говоря уже о том, что оно может иметь побочные эффекты для других тестов и не может использоваться в параллельных тестовых прогонах без ручной синхронизации (поскольку NameValueConnection
не является потокобезопасным).
Это статическое свойство, и Moq предназначен для методов или классов экземпляров Moq, которые можно имитировать с помощью наследования. Другими словами, Moq здесь не поможет.
Для имитации статики я использую бесплатный инструмент Moles . Есть и другие инструменты изоляции фреймворка, такие как Typemock, которые тоже могут это сделать, хотя я считаю, что это платные инструменты.
Когда дело доходит до статики и тестирования, другой вариант - создать статическое состояние самостоятельно, хотя это часто может быть проблематичным (как, я полагаю, это было бы в вашем случае).
И, наконец, если фреймворки изоляции не подходят, и вы привержены этому подходу, фасад, упомянутый Джошуа, является хорошим подходом или любым подходом в целом, когда вы учитываете клиентский код этого отдельно от бизнес-логики, которую вы Использую для проверки.
Я думаю, что написать собственный провайдер app.config - это простая задача, и она более полезна, чем что-либо еще. Особенно вам следует избегать любых подделок, таких как прокладки и т. Д., Потому что, как только вы их используете, Edit & Continue больше не работает.
Провайдеры, которых я использую, выглядят так:
По умолчанию они получают значения из, 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; } }
}
Как насчет того, чтобы просто установить то, что вам нужно? Потому что я не хочу издеваться над .NET, не так ли ...?
System.Configuration.ConfigurationManager.AppSettings["myKey"] = "myVal";
Вероятно, вам следует заранее очистить AppSettings, чтобы убедиться, что приложение видит только то, что вы хотите.