Antworten:
Ich glaube, ein Standardansatz besteht darin , den Konfigurationsmanager mit einem Fassadenmuster zu umhüllen, und dann haben Sie etwas lose gekoppeltes, über das Sie die Kontrolle haben.
Sie würden also den ConfigurationManager einpacken. Etwas wie:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(Sie können einfach eine Schnittstelle aus Ihrer Konfigurationsklasse extrahieren und diese Schnittstelle dann überall in Ihrem Code verwenden.) Dann verspotten Sie einfach die IConfiguration. Möglicherweise können Sie die Fassade selbst auf verschiedene Arten implementieren. Oben habe ich mich entschieden, nur die einzelnen Eigenschaften zu verpacken. Sie erhalten auch den Nebeneffekt, dass Sie stark typisierte Informationen für die Arbeit mit schwach typisierten Hash-Arrays haben.
var configurationMock = new Mock<IConfiguration>();
und für das Setup:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Ich benutze AspnetMvc4. Vor einem Moment habe ich geschrieben
ConfigurationManager.AppSettings["mykey"] = "myvalue";
in meiner Testmethode und es hat perfekt funktioniert.
Erläuterung: Die Testmethode wird in einem Kontext ausgeführt, in dem App-Einstellungen verwendet werden, normalerweise a web.config
oder myapp.config
. ConfigurationsManager
kann dieses anwendungsglobale Objekt erreichen und es manipulieren.
Allerdings: Wenn Sie einen Testläufer haben, der parallel Tests durchführt, ist dies keine gute Idee.
ConfigurationManager.AppSettings
ist eine, NameValueCollection
die nicht threadsicher ist, daher sind parallele Tests, die sie ohne ordnungsgemäße Synchronisation verwenden, sowieso keine gute Idee. Andernfalls können Sie einfach ConfigurationManager.AppSettings.Clear()
Ihren TestInitialize
/ ctor anrufen und Sie sind golden.
Vielleicht ist es nicht das, was Sie erreichen müssen, aber haben Sie darüber nachgedacht, eine app.config in Ihrem Testprojekt zu verwenden? Der ConfigurationManager erhält also die Werte, die Sie in die app.config eingegeben haben, und Sie müssen nichts verspotten. Diese Lösung funktioniert gut für meine Bedürfnisse, da ich nie eine "variable" Konfigurationsdatei testen muss.
Web.config
dem Projekt gezogen. Während des Tests app.config
ist es sehr gültig , einige bekannte Werte aus dem zu ziehen . Der Komponententest muss nur sicherstellen, dass die Bedingungen beim Ziehen von "cluster1" funktionieren. In diesem Fall gibt es immer nur 4 verschiedene Cluster.
Sie können Shims verwenden, um Änderungen AppSettings
an einem benutzerdefinierten NameValueCollection
Objekt vorzunehmen . Hier ist ein Beispiel, wie Sie dies erreichen können:
[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.
}
}
Weitere Informationen zu Shims und Fakes finden Sie unter Isolieren des zu testenden Codes mit Microsoft Fakes . Hoffe das hilft.
Haben Sie darüber nachgedacht, statt zu verspotten? Die AppSettings
Eigenschaft ist ein 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...
}
}
Die Vorteile sind eine einfachere Implementierung und keine Abhängigkeit von System.Configuration, bis Sie es wirklich brauchen.
IConfiguration
wie Joshua Enfield vorschlägt, zu hoch sein, und Sie könnten Fehler übersehen, die aufgrund von Dingen wie schlechtem Parsen von Konfigurationswerten auftreten. Auf der anderen Seite ist die ConfigurationManager.AppSettings
direkte Verwendung, wie LosManos vorschlägt, ein zu großes Implementierungsdetail, ganz zu schweigen davon, dass sie Nebenwirkungen auf andere Tests haben kann und nicht in parallelen Testläufen ohne manuelle Synchronisierung verwendet werden kann (da dies NameValueConnection
nicht threadsicher ist).
Dies ist eine statische Eigenschaft, und Moq wurde für Moq-Instanzmethoden oder -Klassen entwickelt, die über die Vererbung verspottet werden können. Mit anderen Worten, Moq wird Ihnen hier keine Hilfe sein.
Zum Verspotten von Statiken verwende ich ein Tool namens Moles , das kostenlos ist. Es gibt andere Framework-Isolationstools wie Typemock, die dies ebenfalls können, obwohl ich glaube, dass dies kostenpflichtige Tools sind.
Wenn es um Statik und Tests geht, besteht eine andere Möglichkeit darin, den statischen Zustand selbst zu erstellen, obwohl dies oft problematisch sein kann (wie ich mir vorstellen würde, wäre dies in Ihrem Fall der Fall).
Und schließlich, wenn die Isolations-Frameworks keine Option sind und Sie sich diesem Ansatz verpflichtet fühlen, ist die von Joshua erwähnte Fassade ein guter Ansatz oder ein Ansatz im Allgemeinen, bei dem Sie den Client-Code davon von der Geschäftslogik abgrenzen, die Sie verwenden Ich benutze zum Testen.
Ich denke, das Schreiben eines eigenen app.config-Anbieters ist eine einfache Aufgabe und nützlicher als alles andere. Insbesondere sollten Sie Fälschungen wie Unterlegscheiben usw. vermeiden, da Edit & Continue nicht mehr funktioniert, sobald Sie sie verwenden.
Die Anbieter, die ich benutze, sehen folgendermaßen aus:
Standardmäßig erhalten sie die Werte von, App.config
aber für Komponententests kann ich alle Werte überschreiben und sie in jedem Test unabhängig verwenden.
Es sind keine Schnittstellen erforderlich oder werden jedes Mal implementiert. Ich habe eine Dienstprogramm-DLL und verwende diesen kleinen Helfer in vielen Projekten und Unit-Tests.
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; } }
}
Wie wäre es einfach einzustellen, was Sie brauchen? Weil ich .NET nicht verspotten will, oder ...?
System.Configuration.ConfigurationManager.AppSettings["myKey"] = "myVal";
Sie sollten die AppSettings wahrscheinlich vorher bereinigen, um sicherzustellen, dass die App nur das sieht, was Sie möchten.