Risposte:
Credo che un approccio standard a questo sia quello di utilizzare un modello di facciata per avvolgere il gestore di configurazione e quindi si dispone di qualcosa di liberamente accoppiato su cui si ha il controllo.
Quindi avvolgerai il ConfigurationManager. Qualcosa di simile a:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(Puoi semplicemente estrarre un'interfaccia dalla tua classe di configurazione e quindi usare quell'interfaccia ovunque nel tuo codice) Quindi prendi in giro IConfiguration. Potresti essere in grado di implementare la facciata stessa in diversi modi. Sopra ho scelto solo di racchiudere le singole proprietà. Si ottiene anche il vantaggio collaterale di avere informazioni fortemente tipizzate con cui lavorare piuttosto che array di hash di tipo debole.
var configurationMock = new Mock<IConfiguration>();
e per la configurazione:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
Sto usando AspnetMvc4. Un attimo fa ho scritto
ConfigurationManager.AppSettings["mykey"] = "myvalue";
nel mio metodo di prova e ha funzionato perfettamente.
Spiegazione: il metodo di test viene eseguito in un contesto con le impostazioni dell'app prese da, in genere un web.config
o myapp.config
. ConfigurationsManager
può raggiungere questo oggetto globale dell'applicazione e manipolarlo.
Tuttavia: se hai un test runner che esegue i test in parallelo, questa non è una buona idea.
ConfigurationManager.AppSettings
è un programma NameValueCollection
che non è thread-safe, quindi i test paralleli che lo usano senza una corretta sincronizzazione non sono comunque una buona idea. Altrimenti puoi semplicemente chiamare il ConfigurationManager.AppSettings.Clear()
tuo TestInitialize
/ ctor e sei d'oro.
Forse non è quello che devi realizzare, ma hai considerato di utilizzare un app.config nel tuo progetto di test? Quindi il ConfigurationManager otterrà i valori che hai inserito in app.config e non devi deridere nulla. Questa soluzione funziona bene per le mie esigenze, perché non ho mai bisogno di testare un file di configurazione "variabile".
Web.config
del progetto. Durante il test, estrarre alcuni valori ben noti da app.config
è molto valido. Lo unit test deve solo assicurarsi che le condizioni su quando viene eseguito il pull dicano "cluster1" funziona; ci sono sempre e solo 4 diversi cluster in questo caso.
È possibile utilizzare gli spessori per modificare AppSettings
un NameValueCollection
oggetto personalizzato . Ecco un esempio di come puoi ottenere questo risultato:
[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.
}
}
Puoi leggere ulteriori informazioni su shim e falsi su Isolamento del codice sottoposto a test con Microsoft Fakes . Spero che questo ti aiuti.
Hai preso in considerazione lo stubbing invece di deridere? La AppSettings
proprietà è una 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...
}
}
I vantaggi sono un'implementazione più semplice e nessuna dipendenza da System.Configuration fino a quando non ne hai veramente bisogno.
IConfiguration
come suggerisce Joshua Enfield, può essere di livello troppo alto e potresti perdere bug che esistono a causa di cose come una cattiva analisi del valore di configurazione. D'altra parte, l'utilizzo ConfigurationManager.AppSettings
diretto come suggerisce LosManos è un dettaglio di implementazione eccessivo, per non parlare del fatto che può avere effetti collaterali su altri test e non può essere utilizzato in esecuzioni di test parallele senza sincronizzazione manuale (in quanto NameValueConnection
non è thread-safe).
Questa è una proprietà statica e Moq è progettato per metodi o classi di istanza Moq che possono essere derisi tramite ereditarietà. In altre parole, Moq non ti sarà di alcun aiuto qui.
Per deridere la statica, utilizzo uno strumento chiamato Moles , che è gratuito. Ci sono altri strumenti di isolamento del framework, come Typemock che possono farlo anche se credo che siano strumenti a pagamento.
Quando si tratta di statica e test, un'altra opzione è creare lo stato statico da soli, anche se questo può spesso essere problematico (come, immagino che sarebbe nel tuo caso).
E, infine, se i framework di isolamento non sono un'opzione e sei impegnato in questo approccio, la facciata menzionata da Joshua è un buon approccio, o qualsiasi approccio in generale in cui prendi in considerazione il codice del cliente lontano dalla logica di business che tu stai usando per testare.
Penso che scrivere il tuo provider app.config sia un compito semplice ed è più utile di qualsiasi altra cosa. Soprattutto dovresti evitare falsi come spessori, ecc. Perché non appena li usi Modifica e continua non funziona più.
I provider che utilizzo hanno questo aspetto:
Per impostazione predefinita, ottengono i valori da App.config
ma per gli unit test posso sovrascrivere tutti i valori e utilizzarli in ogni test in modo indipendente.
Non c'è bisogno di alcuna interfaccia o implementarla ogni volta più e più volte. Ho una dll di utilità e utilizzo questo piccolo aiuto in molti progetti e unit test.
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; } }
}