Réponses:
Je pense qu'une approche standard consiste à utiliser un modèle de façade pour envelopper le gestionnaire de configuration, puis vous avez quelque chose de faiblement couplé sur lequel vous avez le contrôle.
Donc, vous envelopperiez le ConfigurationManager. Quelque chose comme:
public class Configuration: IConfiguration
{
public User
{
get
{
return ConfigurationManager.AppSettings["User"];
}
}
}
(Vous pouvez simplement extraire une interface de votre classe de configuration, puis utiliser cette interface partout dans votre code) Ensuite, vous vous moquez simplement de l'IConfiguration. Vous pourrez peut-être implémenter la façade elle-même de différentes manières. Ci-dessus, j'ai choisi simplement d'envelopper les propriétés individuelles. Vous obtenez également l'avantage de disposer d'informations fortement typées avec lesquelles travailler plutôt que de tableaux de hachage faiblement typés.
var configurationMock = new Mock<IConfiguration>();
et pour la configuration:configurationMock.SetupGet(s => s.User).Returns("This is what the user property returns!");
J'utilise AspnetMvc4. Il y a un instant j'ai écrit
ConfigurationManager.AppSettings["mykey"] = "myvalue";
dans ma méthode de test et cela a parfaitement fonctionné.
Explication: la méthode de test s'exécute dans un contexte avec des paramètres d'application tirés de, généralement un web.config
ou myapp.config
. ConfigurationsManager
peut atteindre cet objet global d'application et le manipuler.
Cependant: si vous avez un lanceur de tests exécutant des tests en parallèle, ce n'est pas une bonne idée.
ConfigurationManager.AppSettings
est un NameValueCollection
qui n'est pas thread-safe, donc des tests parallèles en l'utilisant sans synchronisation appropriée ne sont de toute façon pas une bonne idée. Sinon, vous pouvez simplement appeler ConfigurationManager.AppSettings.Clear()
votre TestInitialize
/ ctor et vous êtes en or.
Ce n'est peut-être pas ce que vous devez accomplir, mais avez-vous envisagé d'utiliser un app.config dans votre projet de test? Ainsi, le ConfigurationManager obtiendra les valeurs que vous avez mises dans app.config et vous n'avez pas besoin de vous moquer de quoi que ce soit. Cette solution fonctionne bien pour mes besoins, car je n'ai jamais besoin de tester un fichier de configuration "variable".
Web.config
projet englobant. Pendant les tests, extraire des valeurs bien connues de l ' app.config
est très valide. Le test unitaire a juste besoin de s'assurer que les conditions quand il tire disent que "cluster1" fonctionne; il n'y a que 4 clusters différents dans ce cas.
Vous pouvez utiliser des shims pour modifier AppSettings
un NameValueCollection
objet personnalisé . Voici un exemple de la façon dont vous pouvez y parvenir:
[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.
}
}
Vous pouvez en savoir plus sur les shims et les faux à, Isoler le code en cours de test avec Microsoft Fakes . J'espère que cela t'aides.
Avez-vous envisagé de stubbing au lieu de vous moquer? La AppSettings
propriété est un 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...
}
}
Les avantages sont une implémentation plus simple et aucune dépendance à System.Configuration jusqu'à ce que vous en ayez vraiment besoin.
IConfiguration
comme le suggère Joshua Enfield peut être de trop haut niveau, et vous pourriez manquer des bogues qui existent en raison de choses comme une mauvaise analyse de la valeur de configuration. D'un autre côté, utiliser ConfigurationManager.AppSettings
directement comme le suggère LosManos est trop un détail d'implémentation, sans parler qu'il peut avoir des effets secondaires sur d'autres tests et ne peut pas être utilisé dans des tests parallèles sans synchronisation manuelle (ce qui NameValueConnection
n'est pas thread-safe).
Il s'agit d'une propriété statique et Moq est conçu pour les méthodes ou classes d'instance Moq qui peuvent être simulées via l'héritage. En d'autres termes, Moq ne vous sera d'aucune utilité ici.
Pour moquer la statique, j'utilise un outil appelé Moles , qui est gratuit. Il existe d'autres outils d'isolation de framework, comme Typemock, qui peuvent également le faire, même si je pense que ce sont des outils payants.
En ce qui concerne la statique et les tests, une autre option consiste à créer vous-même l'état statique, bien que cela puisse souvent être problématique (comme, j'imagine que ce serait dans votre cas).
Et, enfin, si les frameworks d'isolation ne sont pas une option et que vous êtes engagé dans cette approche, la façade mentionnée par Joshua est une bonne approche, ou toute approche en général où vous factorisez le code client de ceci loin de la logique métier que vous utilisez pour tester.
Je pense que l'écriture de votre propre fournisseur app.config est une tâche simple et est plus utile que toute autre chose. Surtout, vous devriez éviter les faux comme les cales, etc. car dès que vous les utilisez, Edit & Continue ne fonctionne plus.
Les fournisseurs que j'utilise ressemblent à ceci:
Par défaut, ils obtiennent les valeurs du App.config
mais pour les tests unitaires, je peux remplacer toutes les valeurs et les utiliser indépendamment dans chaque test.
Il n'y a pas besoin d'interfaces ou de les implémenter à chaque fois encore et encore. J'ai une DLL d'utilitaires et j'utilise ce petit assistant dans de nombreux projets et tests unitaires.
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; } }
}
Que diriez-vous simplement de définir ce dont vous avez besoin? Parce que je ne veux pas me moquer de .NET, est-ce que je ...?
System.Configuration.ConfigurationManager.AppSettings["myKey"] = "myVal";
Vous devriez probablement nettoyer les AppSettings au préalable pour vous assurer que l'application ne voit que ce que vous voulez.