Test unitaire avec singletons

J’ai préparé des tests automatiques avec le framework de test Visual Studio Team Edition. Je veux que l’un des tests se connecte à la firebase database de la manière habituelle dans le programme:

ssortingng r_providerName = ConfigurationManager.ConnectionSsortingngs["main_db"].ProviderName; 

Mais je reçois une exception dans cette ligne. Je suppose que cela se produit car le ConfigurationManager est un singleton. Comment pouvez-vous contourner le problème des singleton avec les tests unitaires?


Merci pour les réponses. Tous ont été très instructifs.

Consultez le blog Google Testing :

  • Utiliser l’dependency injection pour éviter les singletons
  • Singletons sont des menteurs pathologiques
  • Root Cause of Singletons
  • Où sont passés tous les Singletons?
  • Clean Code Talks – Global State et Singletons
  • Injection de dépendance .

Et aussi:

  • Une fois ne suffit pas
  • Singletons performants

Enfin, Misko Hevery a écrit un guide sur son blog: Writing Testable Code .

Vous pouvez utiliser l’dependency injection de constructeur. Exemple:

 public class SingletonDependedClass { private ssortingng _ProviderName; public SingletonDependedClass() : this(ConfigurationManager.ConnectionSsortingngs["main_db"].ProviderName) { } public SingletonDependedClass(ssortingng providerName) { _ProviderName = providerName; } } 

Cela vous permet de transmettre directement la chaîne de connexion à l’object pendant le test.

En outre, si vous utilisez la structure de test Visual Studio Team Edition, vous pouvez créer un constructeur avec un paramètre privé et tester la classe via l’accesseur.

En fait, je résous ce genre de problèmes avec les moqueries. Exemple:

Vous avez une classe qui dépend de singleton:

 public class Singleton { public virtual ssortingng SomeProperty { get; set; } private static Singleton _Instance; public static Singleton Insatnce { get { if (_Instance == null) { _Instance = new Singleton(); } return _Instance; } } protected Singleton() { } } public class SingletonDependedClass { public void SomeMethod() { ... ssortingng str = Singleton.Insatnce.SomeProperty; ... } } 

SingletonDependedClass doit d’abord être refactorisé pour prendre l’instance de Singleton tant que paramètre constructeur:

 public class SingletonDependedClass { private Singleton _SingletonInstance; public SingletonDependedClass() : this(Singleton.Insatnce) { } private SingletonDependedClass(Singleton singletonInstance) { _SingletonInstance = singletonInstance; } public void SomeMethod() { ssortingng str = _SingletonInstance.SomeProperty; } } 

Test de SingletonDependedClass (la bibliothèque Moq est utilisée):

 [TestMethod()] public void SomeMethodTest() { var singletonMock = new Mock(); singletonMock.Setup(s => s.SomeProperty).Returns("some test data"); var target = new SingletonDependedClass_Accessor(singletonMock.Object); ... } 

Exemple de livre : Travailler efficacement avec le code hérité

Aussi donné la même réponse ici: https://stackoverflow.com/a/28613595/929902

Pour exécuter du code contenant des singletons dans un faisceau de test, nous devons assouplir la propriété singleton. Voici comment nous le faisons. La première étape consiste à append une nouvelle méthode statique à la classe singleton. La méthode nous permet de remplacer l’instance statique dans le singleton. Nous l’appellerons setTestingInstance .

 public class PermitRepository { private static PermitRepository instance = null; private PermitRepository() {} public static void setTestingInstance(PermitRepository newInstance) { instance = newInstance; } public static PermitRepository getInstance() { if (instance == null) { instance = new PermitRepository(); } return instance; } public Permit findAssociatedPermit(PermitNotice notice) { ... } ... } 

Maintenant que nous avons ce setter, nous pouvons créer une instance de test de PermitRepository et la définir. Nous aimerions écrire du code comme celui-ci dans notre configuration de test:

 public void setUp() { PermitRepository repository = PermitRepository.getInstance(); ... // add permits to the repository here ... PermitRepository.setTestingInstance(repository); } 

Vous faites face à un problème plus général ici. En cas d’abus, Singletons empêche la testabilité.

J’ai fait une parsing détaillée de ce problème dans le contexte d’une conception découplée. Je vais essayer de résumer mes points:

  1. Si votre Singleton possède un état global significatif, n’utilisez pas Singleton. Cela inclut le stockage persistant tel que les bases de données, les fichiers, etc.
  2. Dans les cas où la dépendance sur un object Singleton n’est pas évidente par le nom de la classe, la dépendance doit être injectée. La nécessité d’injecter des instances de Singleton dans les classes prouve que le modèle est mal utilisé (voir le point 1).
  3. Le cycle de vie d’un singleton est supposé être le même que celui de l’application. La plupart des implémentations Singleton utilisent un mécanisme de chargement différé pour s’instancier. C’est sortingvial et leur cycle de vie a peu de chance de changer, sinon vous ne devriez pas utiliser Singleton.