Contrôle de l’ordre d’exécution des tests unitaires dans Visual Studio

Ok, j’ai fini de chercher de bonnes informations à ce sujet. J’ai une série de tests unitaires qui appellent une classe statique qui, une fois initialisée, définit les propriétés qui ne peuvent pas (ou que je ne souhaite pas) modifier.

Mon problème est que je ne peux pas appliquer un ordre défini pour que les tests s’exécutent. Si je pouvais, je pourrais les exécuter de manière à ce que les propriétés statiques soient définies de manière fiable, et je pourrais les associer, mais malheureusement, le framework Microsoft.VisualStudio.TestTools.UnitTesting les exécute simplement dans un ordre apparemment aléatoire. .

Donc, j’ai trouvé cette http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.priorityatsortingbute.aspx qui dit dans la section Notes “Cet atsortingbut n’est pas utilisé par le système de test. Il est fourni à l’utilisateur à des fins personnalisées. ” Hein? A quoi ça sert alors? Est-ce qu’ils attendent de moi que j’écrive mon propre wrapper de test pour profiter de cet atsortingbut fabuleux (dont je pourrais facilement écrire moi-même si je voulais aller à ce niveau d’effort …)

Donc, assez de la diasortingbe; En bout de ligne, existe-t-il un moyen de contrôler la commande de mes tests unitaires?

[TestMethod] [Priority(0)] 

etc. ne semble pas fonctionner, ce qui est logique, car Microsoft dit que non.

Aussi, s’il vous plaît pas de commentaires sur “violer l’isolement”. TestClass isole ce que je teste, pas les méthodes de test individuelles. Quoi qu’il en soit, chaque test peut être exécuté indépendamment, tout simplement, ils ne peuvent pas être exécutés ensemble dans un ordre aléatoire car il n’y a aucun moyen de détruire la classe statique.

Oh, je connais aussi le “Tested Test”.

Fusionner vos tests en un seul test géant fonctionnera. Pour rendre la méthode de test plus lisible, vous pouvez faire quelque chose comme:

 [TestMethod] public void MyIntegratonTestLikeUnitTest() { AssertScenarioA(); AssertScenarioB(); .... } private void AssertScenarioA() { // Assert } private void AssertScenarioB() { // Assert } 

En fait, le problème que vous vous posez suggère probablement d’améliorer la testabilité de l’implémentation.

Vous pouvez utiliser la liste de lecture

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

l’ordre d’exécution sera comme vous les ajoutez à la playlist mais si vous voulez le changer vous avez le fichier

entrer la description de l'image ici

Je ne vois personne mentionner la méthode d’atsortingbut ClassInitialize. Les atsortingbuts sont assez simples.

Créez des méthodes marquées avec l’atsortingbut [ClassInitialize ()] ou [TestInitialize ()] pour préparer des aspects de l’environnement dans lequel votre test unitaire s’exécutera. Le but de ceci est d’établir un état connu pour exécuter votre test unitaire. Par exemple, vous pouvez utiliser la méthode [ClassInitialize ()] ou [TestInitialize ()] pour copier, modifier ou créer certains fichiers de données que votre test utilisera.

Créez des méthodes marquées avec l’atsortingbut [ClassCleanup ()] ou [TestCleanUp {}] pour renvoyer l’environnement à un état connu après l’exécution d’un test. Cela peut signifier la suppression de fichiers dans des dossiers ou le retour d’une firebase database à un état connu. Un exemple de ceci est de réinitialiser une firebase database d’inventaire à un état initial après avoir testé une méthode utilisée dans une application de saisie de commande.

[ClassInitialize ()] Utilisez ClassInitialize pour exécuter le code avant d’exécuter le premier test de la classe.

[ClassCleanUp ()] Utilisez Class Cleanup pour exécuter du code après que tous les tests d’une classe ont été exécutés.

[TestInitialize ()] Utilisez TestInitialize pour exécuter le code avant d’exécuter chaque test.

[TestCleanUp ()] Utilisez TestCleanup pour exécuter du code après chaque test.

Comme les commentateurs l’ont déjà souligné, le fait que les tests dépendent d’autres tests indique un défaut de conception. Néanmoins, il existe des moyens d’y parvenir. Comme il a été répondu dans une question posée précédemment, vous pouvez créer des tests unitaires ordonnés, qui sont fondamentalement un conteneur de test unique qui assure la séquence de test.

Voici un guide sur MSDN: http://msdn.microsoft.com/en-us/library/ms182631.aspx

Étant donné que vous avez déjà mentionné la fonctionnalité de test commandé que Visual Studio teste, je l’ignorerai. Vous semblez également être conscient que ce que vous essayez d’accomplir pour tester cette classe statique est une “mauvaise idée”, alors je l’ignorerai.

À la place, concentrons-nous sur la manière dont vous pourriez réellement garantir que vos tests sont exécutés dans l’ordre souhaité. Une option (fournie par @gaog) est “une méthode de test, de nombreuses fonctions de test”, appelant vos fonctions de test dans l’ordre de votre TestMethod l’intérieur d’une seule fonction marquée par l’atsortingbut TestMethod . C’est le moyen le plus simple, et le seul inconvénient est que la première fonction de test échouera à empêcher l’exécution de l’une des fonctions de test restantes .

Avec votre description de la situation, c’est la solution que je vous suggère d’utiliser.

Si la partie en gras est un problème pour vous, vous pouvez effectuer une exécution ordonnée des tests isolés en tirant parti de la fonctionnalité de test intégrée des données. C’est plus compliqué et se sent un peu sale, mais le travail est fait.

En bref, vous définissez une source de données (comme un fichier CSV ou une table de firebase database) qui contrôle l’ordre dans lequel vous devez exécuter vos tests et les noms des fonctions qui contiennent réellement la fonctionnalité de test. Vous connectez ensuite cette source de données à un test basé sur des données, utilisez l’option de lecture séquentielle et exécutez vos fonctions, dans l’ordre souhaité, sous forme de tests individuels.

 [TestClass] public class OrderedTests { public TestContext TestContext { get; set; } private const ssortingng _OrderedTestFilename = "TestList.csv"; [TestMethod] [DeploymentItem(_OrderedTestFilename)] [DataSource("Microsoft.VisualStudio.TestTools.DataSource.CSV", _OrderedTestFilename, _OrderedTestFilename, DataAccessMethod.Sequential)] public void OrderedTests() { var methodName = (ssortingng)TestContext.DataRow[0]; var method = GetType().GetMethod(methodName); method.Invoke(this, new object[] { }); } public void Method_01() { Assert.IsTrue(true); } public void Method_02() { Assert.IsTrue(false); } public void Method_03() { Assert.IsTrue(true); } } 

Dans mon exemple, j’ai un fichier de support appelé TestList.csv, qui est copié en sortie. Cela ressemble à ceci:

 TestName Method_01 Method_02 Method_03 

Vos tests seront exécutés dans l’ordre que vous avez spécifié et dans l’isolement de test normal (par exemple, en cas d’échec, le rest est exécuté, mais en partageant des classes statiques).

Ce qui précède n’est vraiment que l’idée de base, si je devais l’utiliser en production, je générerais dynamicment les noms des fonctions de test et leur ordre avant l’exécution du test. Peut-être en exploitant PriorityAtsortingbute trouvé et un code de reflection simple pour extraire les méthodes de test de la classe et les ordonner de manière appropriée, puis écrivez cette commande dans la source de données.

Voici une classe qui peut être utilisée pour configurer et exécuter des tests ordonnés indépendamment du framework MS Ordered Tests pour quelque raison que ce soit – par exemple, ne pas avoir à ajuster les arguments mstest.exe sur une machine de compilation ou à les classer avec une classe non ordonnée.

Le framework de test d’origine ne voit la liste des tests ordonnés que comme un seul test, donc tout init / cleanup comme [TestInitalize ()] Init () est uniquement appelé avant et après l’ensemble.

Usage:

  [TestMethod] // place only on the list--not the individuals public void OrderedStepsTest() { OrderedTest.Run(TestContext, new List { new OrderedTest ( T10_Reset_Database, false ), new OrderedTest ( T20_LoginUser1, false ), new OrderedTest ( T30_DoLoginUser1Task1, true ), // continue on failure new OrderedTest ( T40_DoLoginUser1Task2, true ), // continue on failure // ... }); } 

La mise en oeuvre:

 using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; namespace UnitTests.Utility { ///  /// Define and Run a list of ordered tests. /// 2016/08/25: Posted to SO by crokusek ///  public class OrderedTest { /// Test Method to run public Action TestMethod { get; private set; } /// Flag indicating whether testing should continue with the next test if the current one fails public bool ContinueOnFailure { get; private set; } /// Any Exception thrown by the test public Exception ExceptionResult; ///  /// Constructor ///  ///  /// True to continue with the next test if this test fails public OrderedTest(Action testMethod, bool continueOnFailure = false) { TestMethod = testMethod; ContinueOnFailure = continueOnFailure; } ///  /// Run the test saving any exception within ExceptionResult /// Throw to the caller only if ContinueOnFailure == false ///  ///  public void Run() { try { TestMethod(); } catch (Exception ex) { ExceptionResult = ex; throw; } } ///  /// Run a list of OrderedTest's ///  static public void Run(TestContext testContext, List tests) { Stopwatch overallStopWatch = new Stopwatch(); overallStopWatch.Start(); List exceptions = new List(); int testsAttempted = 0; for (int i = 0; i < tests.Count; i++) { OrderedTest test = tests[i]; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); testContext.WriteLine("Starting ordered test step ({0} of {1}) '{2}' at {3}...\n", i + 1, tests.Count, test.TestMethod.Method, DateTime.Now.ToString("G")); try { testsAttempted++; test.Run(); } catch { if (!test.ContinueOnFailure) break; } finally { Exception testEx = test.ExceptionResult; if (testEx != null) // capture any "continue on fail" exception exceptions.Add(testEx); testContext.WriteLine("\n{0} ordered test step {1} of {2} '{3}' in {4} at {5}{6}\n", testEx != null ? "Error: Failed" : "Successfully completed", i + 1, tests.Count, test.TestMethod.Method, stopWatch.ElapsedMilliseconds > 1000 ? (stopWatch.ElapsedMilliseconds * .001) + "s" : stopWatch.ElapsedMilliseconds + "ms", DateTime.Now.ToSsortingng("G"), testEx != null ? "\nException: " + testEx.Message + "\nStackTrace: " + testEx.StackTrace + "\nContinueOnFailure: " + test.ContinueOnFailure : ""); } } testContext.WriteLine("Completed running {0} of {1} ordered tests with a total of {2} error(s) at {3} in {4}", testsAttempted, tests.Count, exceptions.Count, DateTime.Now.ToSsortingng("G"), overallStopWatch.ElapsedMilliseconds > 1000 ? (overallStopWatch.ElapsedMilliseconds * .001) + "s" : overallStopWatch.ElapsedMilliseconds + "ms"); if (exceptions.Any()) { // Test Explorer prints better msgs with this hierarchy rather than using 1 AggregateException(). throw new Exception(Ssortingng.Join("; ", exceptions.Select(e => e.Message), new AggregateException(exceptions))); } } } } 

Je ne vais pas aborder l’ordre des tests, désolé. D’autres l’ont déjà fait. En outre, si vous connaissez les “tests ordonnés” – eh bien, ceci est la réponse de MS VS au problème. Je sais que ces tests ordonnés ne sont pas amusants. Mais ils pensaient que ce serait “ça” et il n’y a vraiment rien de plus à ce sujet dans MSTest.

J’écris sur l’une de vos hypothèses:

comme il n’y a aucun moyen de détruire la classe statique.

À moins que votre classe statique ne représente un état externe à l’ensemble du processus externe à votre code (par exemple, l’état d’une bibliothèque DLL native non gérée, ce qui there is no way invoqué par le rest de votre code), votre hypothèse there is no way .

Si votre classe statique se réfère à cela, alors désolé, vous avez parfaitement raison, le rest de cette réponse est sans importance. Cependant, comme vous n’avez pas dit cela, je suppose que votre code est “géré”.

Pensez et vérifiez le truc AppDomain. Rarement, c’est nécessaire, mais c’est exactement le cas lorsque vous souhaitez probablement les utiliser.

Vous pouvez créer un nouvel AppDomain, instancier le test à cet endroit et y exécuter la méthode de test. Les données statiques utilisées par le code managé y seront isolées et à la fin, vous pourrez décharger AppDomain et toutes les données, statiques incluses, s’évaporeront. Ensuite, le test suivant initialiserait un autre domaine d’application, etc.

Cela fonctionnera sauf si vous avez un état externe que vous devez suivre. Les AppDomains isolent uniquement la mémoire gérée. Toute DLL native sera toujours chargée par processus et leur état sera partagé par tous les AppDomains.

En outre, créer / supprimer les domaines d’application ralentira les tests. En outre, vous pouvez avoir des problèmes avec la résolution d’assemblage dans le domaine d’application enfant, mais ils peuvent être résolus avec une quantité raisonnable de code réutilisable.

En outre, vous pouvez rencontrer de petits problèmes lors de la transmission des données de test vers et depuis l’application AppDomain enfant. Les objects passés devront soit être sérialisables d’une manière ou d’une autre, soit être MarshalByRef ou etc.

Cependant, faites attention ici, ce sera une conversation gérée à 100%. Si vous prenez des précautions supplémentaires et ajoutez un peu de travail à la configuration d’AppDomain, vous pourrez même passer des delegates et les exécuter dans le domaine cible. Ensuite, au lieu de procéder à une configuration poilue entre domaines, vous pouvez intégrer vos tests à quelque chose comme:

 void testmethod() { TestAppDomainHelper.Run( () => { // your test code }); } 

ou même

 [IsolatedAppDomain] void testmethod() { // your test code } 

si votre infrastructure de test prend en charge la création de tels wrappers / extensions. Après quelques recherches et travaux initiaux, leur utilisation est presque sortingviale.

Comme vous devez le savoir maintenant, les puristes disent qu’il est interdit d’exécuter des tests commandés. Cela pourrait être vrai pour les tests unitaires. MSTest et d’autres frameworks de tests unitaires sont utilisés pour exécuter des tests unitaires purs, mais aussi des tests d’interface utilisateur, des tests d’intégration complets, etc. Peut-être ne devrions-nous pas les appeler des frameworks de tests unitaires, ou peut-être devrions-nous les utiliser et les utiliser selon nos besoins. C’est ce que font la plupart des gens.

Je suis en cours d’exécution VS2015 et je dois exécuter des tests dans un ordre donné parce que je cours des tests d’interface utilisateur (Selenium).

Priorité – Ne fait rien du tout Cet atsortingbut n’est pas utilisé par le système de test. Il est fourni à l’utilisateur à des fins personnalisées.

un ordre ordonné – ça marche mais je ne le recommande pas parce que:

  1. Un test ordonné d’ un fichier texte qui répertorie vos tests dans l’ordre dans lequel ils doivent être exécutés. Si vous modifiez un nom de méthode, vous devez corriger le fichier.
  2. L’ordre d’exécution du test est respecté à l’intérieur d’une classe. Vous ne pouvez pas commander quelle classe exécute ses tests en premier.
  3. Un fichier de test ordonné est lié à une configuration, soit Debug ou Release
  4. Vous pouvez avoir plusieurs fichiers de test mais une méthode donnée ne peut pas être répétée dans différents fichiers de test . Vous ne pouvez donc pas avoir un fichier de test pour Debug et un autre pour Release.

D’autres suggestions dans ce sujet sont intéressantes, mais vous perdez la possibilité de suivre les progrès du test dans Test Explorer.

Vous vous retrouvez avec la solution que purist conseillera, mais en fait, c’est la solution qui fonctionne: sortingez par ordre de déclaration .

L’exécutant MSTest utilise un interop qui parvient à obtenir l’ordre de déclaration et cette astuce fonctionnera jusqu’à ce que Microsoft modifie le code de l’exécuteur de test.

Cela signifie que la méthode de test qui est déclarée en premier lieu est exécutée avant celle déclarée à la deuxième place, etc.

Pour vous simplifier la vie, l’ordre de déclaration doit correspondre à l’ordre alphabétique affiché dans l’Explorateur de tests.

  • A010_FirstTest
  • A020_SecondTest
  • etc
  • A100_TenthTest

Je suggère fortement des règles anciennes et éprouvées:

  • utilisez une étape de 10 car vous devrez insérer une méthode de test ultérieurement
  • évite d’avoir à renuméroter vos tests en utilisant un pas important entre les numéros de test
  • utiliser 3 chiffres pour numéroter vos tests si vous exécutez plus de 10 tests
  • utiliser 4 chiffres pour numéroter vos tests si vous exécutez plus de 100 tests

TRÈS IMPORTANT

Pour exécuter les tests par l’ordre de déclaration, vous devez utiliser Run All dans l’Explorateur de tests.

Disons que vous avez 3 classes de test (dans mon cas, tests pour Chrome, Firefox et Edge). Si vous sélectionnez une classe donnée et cliquez avec le bouton droit de la souris sur Exécuter les tests sélectionnés, elle commence généralement par exécuter la méthode déclarée au dernier endroit.

Encore une fois, comme je l’ai déjà dit, l’ ordre déclaré et l’ ordre listé devraient correspondre, sinon vous aurez de gros problèmes en un rien de temps.

ils ne peuvent simplement pas être exécutés ensemble dans un ordre aléatoire car il n’y a aucun moyen de détruire la classe statique

Vous pouvez nommer les espaces de noms et les classes par ordre alphabétique. par exemple.:

  • MonApp.Test. Stage01 _Setup. Step01 _BuildDB
  • MonApp.Test. Stage01 _Setup. Step02 _UpgradeDB
  • MonApp.Test. Stage02 _Domain. Step01 _TestMyStaff
  • MonApp.Test. Stage03 _Intégration. Step01 _TestMyStaff

MyApp.Test.Stage01_Setup est un espace de noms et Step01_BuildDB est un nom de classe.