Exécuter des tests unitaires en série (plutôt qu’en parallèle)

J’essaie de tester à l’unité le moteur de gestion d’hôte WCF que j’ai écrit. Le moteur crée essentiellement des instances ServiceHost à la volée en fonction de la configuration. Cela nous permet de reconfigurer de manière dynamic les services disponibles sans avoir à les supprimer et à les redémarrer chaque fois qu’un nouveau service est ajouté ou un ancien supprimé.

J’ai rencontré une difficulté lors des tests unitaires de ce moteur de gestion d’hôte, en raison du fonctionnement de ServiceHost. Si un ServiceHost a déjà été créé, ouvert et pas encore fermé pour un noeud final particulier, un autre ServiceHost pour le même noeud final ne peut pas être créé, ce qui entraîne une exception. Étant donné que les plates-formes de tests unitaires modernes parallélisent leur exécution, je ne dispose d’aucun moyen efficace pour tester ce morceau de code.

J’ai utilisé xUnit.NET, espérant pouvoir, à cause de son extensibilité, trouver un moyen de le forcer à exécuter les tests en série. Cependant, je n’ai pas eu de chance. J’espère que quelqu’un sur SO a rencontré un problème similaire et sait comment exécuter les tests unitaires en série.

REMARQUE: ServiceHost est une classe WCF, écrite par Microsoft. Je n’ai pas la capacité de changer son comportement. Le comportement approprié de chaque sharepoint terminaison de service est également approprié … Toutefois, il n’est pas particulièrement propice aux tests unitaires.

Comme indiqué ci-dessus, tous les bons tests unitaires doivent être isolés à 100%. L’utilisation de l’état partagé (par exemple, en fonction d’une propriété static modifiée par chaque test) est considérée comme une mauvaise pratique.

Cela dit, votre question sur l’exécution séquentielle de tests xUnit a une réponse! J’ai rencontré exactement le même problème car mon système utilise un localisateur de services statique (ce qui est moins qu’idéal).

Par défaut, xUnit 2.x exécute tous les tests en parallèle. Cela peut être modifié par assemblage en définissant CollectionBehavior dans votre AssemblyInfo.cs de votre projet de test.

Pour la séparation par assemblage, utilisez:

 using Xunit; [assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)] 

ou pour aucune parallélisation du tout utiliser:

 [assembly: CollectionBehavior(DisableTestParallelization = true)] 

Ce dernier est probablement celui que vous voulez. Vous trouverez plus d’informations sur la parallélisation et la configuration dans la documentation de xUnit .

Pour les projets .NET Core, créez xunit.runner.json avec:

 { "parallelizeAssembly": false, "parallelizeTestCollections": false } 

De plus, votre project.json devrait contenir

 "buildOptions": { "copyToOutput": { "include": [ "xunit.runner.json" ] } } 

Chaque classe de test constitue une collection de tests unique. Les tests correspondants s’exécutent en séquence. Par conséquent, si vous placez tous vos tests dans la même collection, ils seront exécutés de manière séquentielle.

Dans xUnit, vous pouvez apporter les modifications suivantes pour y parvenir:

La suite suivra en parallèle:

 namespace IntegrationTests { public class Class1 { [Fact] public void Test1() { Console.WriteLine("Test1 called"); } [Fact] public void Test2() { Console.WriteLine("Test2 called"); } } public class Class2 { [Fact] public void Test3() { Console.WriteLine("Test3 called"); } [Fact] public void Test4() { Console.WriteLine("Test4 called"); } } } 

Pour le rendre séquentiel, il vous suffit de placer les deux classes de test dans la même collection:

 namespace IntegrationTests { [Collection("Sequential")] public class Class1 { [Fact] public void Test1() { Console.WriteLine("Test1 called"); } [Fact] public void Test2() { Console.WriteLine("Test2 called"); } } [Collection("Sequential")] public class Class2 { [Fact] public void Test3() { Console.WriteLine("Test3 called"); } [Fact] public void Test4() { Console.WriteLine("Test4 called"); } } } 

Pour plus d’informations, vous pouvez vous référer à ce lien

Pour les projets .NET Core, vous pouvez configurer xUnit avec un fichier xunit.runner.json , comme xunit.runner.json à l’ adresse https://xunit.github.io/docs/configuring-with-json.html .

Le paramètre que vous devez modifier pour arrêter l’exécution du test en parallelizeTestCollections est parallelizeTestCollections , dont la valeur par défaut est true :

Définissez cette propriété sur true si l’assemblage souhaite exécuter des tests dans cet assemblage en parallèle les uns par rapport aux autres. … Affectez la valeur false à cette false pour désactiver toute la parallélisation dans cet assemblage de test.

Type de schéma JSON: booléen
Valeur par défaut: true

Donc, un xunit.runner.json minimal à cette fin ressemble à

 { "parallelizeTestCollections": false } 

Comme indiqué dans la documentation, n’oubliez pas d’inclure ce fichier dans votre construction, en:

  • Définition de Copier dans le répertoire de sortie pour Copier s’il est plus récent dans les propriétés du fichier dans Visual Studio ou
  • Ajouter

      PreserveNewest  

    dans votre fichier .csproj , ou

  • Ajouter

     "buildOptions": { "copyToOutput": { "include": [ "xunit.runner.json" ] } } 

    dans votre fichier project.json

en fonction de votre type de projet.

Enfin, en plus de ce qui précède, si vous utilisez Visual Studio, assurez-vous de ne pas avoir cliqué par inadvertance sur le bouton Exécuter les tests en parallèle , ce qui entraînera l’exécution des tests en parallèle, même si vous avez désactivé la parallélisation dans xunit.runner.json . Les concepteurs d’interface utilisateur de Microsoft ont habilement fait en sorte que ce bouton ne soit pas étiqueté, difficile à remarquer et à environ un centimètre du bouton “Run All” (Exécuter tout) de Test Explorer, afin de maximiser les chances que vous le frappiez par erreur et que vous ne sachiez pas pourquoi vos tests échouent soudainement:

Capture d'écran avec le bouton encerclé

vous pouvez utiliser la playlist

clic droit sur la méthode de test -> Ajouter à la playlist -> Nouvelle playlist

alors vous pouvez spécifier l’ordre d’exécution, la valeur par défaut est, lorsque vous les ajoutez à la liste de lecture, mais vous pouvez modifier le fichier de liste de lecture à votre guise

entrez la description de l'image ici

Je ne connais pas les détails, mais il semblerait que vous essayiez peut-être de faire des tests d’intégration plutôt que des tests unitaires . Si vous pouviez isoler la dépendance à l’égard de ServiceHost , vos tests seraient probablement plus faciles (et plus rapides). Donc, par exemple, vous pouvez tester indépendamment les éléments suivants:

  • Cours de lecture de configuration
  • Usine ServiceHost (éventuellement comme test d’intégration)
  • Classe de moteur prenant un IServiceHostFactory et une IConfiguration

Les outils permettant d’inclure des structures d’isolation (mocking) et (éventuellement) des conteneurs IoC. Voir:

Peut-être que vous pouvez utiliser les tests unitaires avancés . Il vous permet de définir la séquence dans laquelle vous exécutez le test . Vous devrez donc peut-être créer un nouveau fichier cs pour héberger ces tests.

Voici comment vous pouvez combiner les méthodes de test pour qu’elles fonctionnent dans l’ordre que vous souhaitez.

 [Test] [Sequence(16)] [Requires("POConstructor")] [Requires("WorkOrderConstructor")] public void ClosePO() { po.Close(); // one charge slip should be added to both work orders Assertion.Assert(wo1.ChargeSlipCount==1, "First work order: ChargeSlipCount not 1."); Assertion.Assert(wo2.ChargeSlipCount==1, "Second work order: ChargeSlipCount not 1."); ... } 

Faites-moi savoir si cela fonctionne.