Odpowiedzi:
Uważam, że jednym standardowym podejściem do tego jest użycie wzorca fasady do owinięcia menedżera konfiguracji, a wtedy masz coś luźno powiązanego, nad którym masz kontrolę.
Więc opakowałbyś plik ConfigurationManager. Coś jak:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(Możesz po prostu wyodrębnić interfejs z klasy konfiguracji, a następnie użyć tego interfejsu wszędzie w kodzie). Następnie po prostu mockujesz IConfiguration. Możesz być w stanie zaimplementować samą elewację na kilka różnych sposobów. Powyżej wybrałem po prostu zawinięcie poszczególnych właściwości. Dodatkową korzyścią jest również posiadanie silnie wpisanych informacji do pracy, a nie słabo wpisanych tablic mieszających.
var configurationMock = new Mock<IConfiguration>();
i dla konfiguracji:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Używam AspnetMvc4. Przed chwilą pisałem
ConfigurationManager.AppSettings["mykey"] = "myvalue";
w mojej metodzie testowej i zadziałało idealnie.
Objaśnienie: metoda testowa działa w kontekście z ustawieniami aplikacji pobranymi z, zwykle a web.config
lub myapp.config
. ConfigurationsManager
może dotrzeć do tego globalnego obiektu aplikacji i nim manipulować.
Chociaż: jeśli masz biegacza testowego, który równolegle przeprowadza testy, nie jest to dobry pomysł.
ConfigurationManager.AppSettings
jest NameValueCollection
który nie jest bezpieczny dla wątków, więc równoległe testy z jego użyciem bez odpowiedniej synchronizacji i tak nie są dobrym pomysłem. W przeciwnym razie możesz po prostu sprawdzić ConfigurationManager.AppSettings.Clear()
swojego TestInitialize
/ ctora i jesteś złoty.
Może nie jest to to, co musisz osiągnąć, ale czy rozważałeś użycie pliku app.config w swoim projekcie testowym? Więc ConfigurationManager otrzyma wartości, które umieścisz w app.config i nie musisz niczego mockować. To rozwiązanie działa dobrze na moje potrzeby, ponieważ nigdy nie muszę testować pliku konfiguracyjnego „zmiennej”.
Web.config
obejmującego. Podczas testowania pobieranie niektórych dobrze znanych wartości z programu app.config
jest bardzo ważne. Test jednostkowy musi tylko upewnić się, że warunki w momencie ściągania działają, na przykład „klaster1”; w tym przypadku istnieją tylko 4 różne klastry.
Możesz użyć podkładek, aby zmodyfikować obiekt AppSettings
niestandardowy NameValueCollection
. Oto przykład, jak możesz to osiągnąć:
[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.
}
}
Więcej informacji na temat podkładek i podróbek można znaleźć pod adresem Isolating Code Under Test with Microsoft Fakes . Mam nadzieję że to pomoże.
Czy zastanawiałeś się nad kiciem zamiast kpiny? PlikAppSettings
Obiekt jest 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...
}
}
Korzyści to prostsza implementacja i brak zależności od System.Configuration, dopóki naprawdę tego nie potrzebujesz.
IConfiguration
jak sugeruje Joshua Enfield, może być zbyt wysokim poziomem i możesz przegapić istniejące błędy z powodu takich rzeczy, jak złe analizowanie wartości konfiguracji. Z drugiej strony, używanie ConfigurationManager.AppSettings
bezpośrednio, jak sugeruje LosManos, jest zbyt dużym szczegółem implementacji, nie wspominając o tym, że może mieć skutki uboczne w innych testach i nie może być używane w równoległych przebiegach testów bez ręcznej synchronizacji (ponieważ NameValueConnection
nie jest bezpieczne dla wątków).
To jest właściwość statyczna, a Moq jest przeznaczony do metod lub klas instancji Moq, które mogą być mockowane przez dziedziczenie. Innymi słowy, Moq nie pomoże ci tutaj.
Do kpiny ze statyki używam narzędzia o nazwie Moles , które jest bezpłatne. Istnieją inne narzędzia do izolacji struktury, takie jak Typemock, które również mogą to zrobić, chociaż uważam, że są to narzędzia płatne.
Jeśli chodzi o statykę i testowanie, inną opcją jest samodzielne utworzenie stanu statycznego, chociaż często może to być problematyczne (tak jak wyobrażam sobie, że byłoby to w twoim przypadku).
I wreszcie, jeśli ramy izolacji nie wchodzą w grę i jesteś oddany temu podejściu, fasada, o której wspomniał Joshua, jest dobrym podejściem lub ogólnie dowolnym podejściem, w którym kod klienta należy oddzielić od logiki biznesowej, którą używam do testowania.
Myślę, że napisanie własnego dostawcy app.config jest prostym zadaniem i jest bardziej przydatne niż cokolwiek innego. Szczególnie należy unikać wszelkich podróbek, takich jak podkładki itp., Ponieważ gdy tylko ich użyjesz, Edycja i Kontynuuj przestaje działać.
Dostawcy, których używam, wyglądają tak:
Domyślnie pobierają wartości z, App.config
ale w przypadku testów jednostkowych mogę zastąpić wszystkie wartości i używać ich w każdym teście niezależnie.
Nie ma potrzeby stosowania żadnych interfejsów ani implementowania go za każdym razem. Mam bibliotekę DLL z narzędziami i używam tego małego pomocnika w wielu projektach i testach jednostkowych.
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; } }
}