web-dev-qa-db-fra.com

Comment exécuter des tests unitaires (MSTest) en parallèle?

Je cherche des moyens d'exécuter des suites de tests en parallèle.

Je suis conscient de .testrunconfig réglage. Cela vous permet de multiplexer sur le nombre de CPU.

Je veux exécuter 1000 tests en parallèle. Cela a du sens car je teste un service Web, donc 90% du temps passé dans un test attend que le service réponde.

Des idées sur la façon de retirer cela? Les tests sont écrits pour VS, mais je suis prêt à les exécuter en dehors de VS.

Édition ultérieure: l'équipe de test de Visual Studio l'a ajouté dans la mise à jour VS 2015 1. Voir la réponse de Mark Sowul ci-dessous.

55
Bogdan Gavril MSFT

La plupart des réponses sur cette page oublient de mentionner que MSTest parallélise les tests dans des assemblages séparés . Vous devez diviser vos unités de test en plusieurs fichiers .dll pour les paralyser.

Mais! La version récente - MSTest V2 - maintenant [~ # ~] peut [~ # ~] paralléliser "in-Assembly" (yay!) Dont vous avez juste besoin pour installer quelques packages de nuget dans votre projet de test - TestFramework et TestAdapter - comme décrit ici https://blogs.msdn.Microsoft.com/devops/2018/01/30/mstest-v2-in-Assembly -parallel-test-execution /

Et puis ajoutez simplement ceci à votre projet de test

[Assembly: Parallelize(Workers = 4, Scope = ExecutionScope.ClassLevel)]

EDIT: vous pouvez également désactiver l'exécution parallèle pour un test spécifique à l'aide de [DoNotParallelize] sur une méthode de test.

25
Alex

Vous pouvez obtenir jusqu'à 5 en utilisant la méthode à partir du blog de test de Visual Studio Team

Gardez à l'esprit qu'il peut y avoir des problèmes de concurrence en utilisant cela, car MSTest n'isole pas complètement chaque test (les statistiques se reportent, par exemple, ce qui rend les choses intéressantes pour le code destiné à être exécuté une fois).

(Aucune idée de la raison pour laquelle la limite est 5, mais MSTest ne les exécutera pas en parallèle si parallelTestCount est défini sur plus de 5. Selon les commentaires ci-dessous, cette règle change apparemment avec Visual Studio 2013)

29
Rangoric

Visual Studio 2015 Update 1 ajoute cela. https://docs.Microsoft.com/visualstudio/releasenotes/vs2015-update1-vs#misc

Pour la mise à jour 2, il y a un bouton bascule d'interface utilisateur dans la barre d'outils en haut du volet de l'Explorateur de tests (entre les zones "regroupement" et "recherche").

Pour la mise à jour 1, définissez les éléments suivants dans les .runsettings

<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
  <RunConfiguration>
    <MaxCpuCount>0</MaxCpuCount>
   </RunConfiguration>
</RunSettings>

La valeur de MaxCpuCount a la sémantique suivante:

• ‘n’ (où 1 <= n <= nombre de cœurs): jusqu’à ‘n’ processus seront lancés.

• "n" de toute autre valeur: le nombre de processus lancés sera autant que les cœurs disponibles sur la machine.

21
Mark Sowul

Ce que j'ai trouvé, c'est que C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe exécutera des tests parallèles avec un .testsettings fichier qui ressemble à ceci:

<?xml version="1.0" encoding="UTF-8"?>
<TestSettings name="TestSettings1" id="21859d0f-7bdc-4165-b9ad-05fc803c9ee9" xmlns="http://Microsoft.com/schemas/VisualStudio/TeamTest/2010">
  <Description>These are default test settings for a local test run.</Description>
  <Deployment enabled="false" />
  <Execution parallelTestCount="8">
    <TestTypeSpecific>
      <UnitTestRunConfig testTypeId="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b">
        <AssemblyResolution>
          <TestDirectory useLoadContext="true" />
        </AssemblyResolution>
      </UnitTestRunConfig>
    </TestTypeSpecific>
    <AgentRule name="Execution Agents">
    </AgentRule>
  </Execution>
</TestSettings>

La référence peut être trouvée ici http://msdn.Microsoft.com/en-us/library/vstudio/jj155796.aspx

8
Igor

Les réponses ci-dessus ont certainement aidé à clarifier les choses pour moi, mais ce point du blog de John Koerner: https://johnkoerner.com/vs2015/parallel-test-execution-in-visual-studio-2015-update-1 -might-not-be-what-you-expect / était le peu qui nous manquait.

"L'exécution de test parallèle exploite les cœurs disponibles sur la machine et est réalisée en lançant le moteur d'exécution de test sur chaque cœur disponible en tant que processus distinct et en lui remettant un conteneur (assemblage, DLL ou artefact pertinent contenant les tests à exécuter), valeur de tests à exécuter. "

-> "Le bit de conteneur séparé est la pièce qui me manquait. Pour que mes tests s'exécutent en parallèle, je devais diviser mes tests en assemblages de test séparés. Après cela, j'ai vu que les tests dans différents assemblages fonctionnaient en parallèle. "

Donc oui, nous avons fait exécuter les tests en parallèle dans VSTS en utilisant leur indicateur pratique "exécuter en parallèle", mais cela ne suffisait pas, nous avons dû diviser nos tests en projets de test séparés. Logiquement regroupé bien sûr, pas un projet par test qui serait ridicule

5
JPThorne
  1. Assurez-vous que la première colonne de votre DataTable est un ID unique.
  2. Créez un délégué AsyncExecutionTask qui accepte un DataRow et ne renvoie rien.
  3. Créez une classe statique (ParallelTesting) avec une méthode AsyncExecutionContext qui accepte un DataRow et un délégué AsyncExecutionTask.
  4. Dans la classe statique, ajoutez une propriété BatchStarted statique.
  5. Dans la classe statique, ajoutez une propriété de dictionnaire AsyncExecutionTests statique.
  6. Dans la méthode AsyncExecutionContext, ajoutez ce qui suit:

    public static void AsyncExecutionContext(DataRow currentRow, AsyncExecutionTask test) 
    {
        if(!BatchStarted)
        {
            foreach(DataRow row in currentRow.Table)
            {
                Task testTask = new Task(()=> { test.Invoke(row); });
                AsyncExecutionTests.Add(row[0].ToString(), testTask);
                testTask.Start();
            }
            BatchStarted = true;
        }
        Task currentTestTask = AsyncExecutionTests[row[0].ToString()];
        currentTestTask.Wait();
        if(currentTestTask.Exception != null) throw currentTestTask.Exception;
    }
    
  7. Maintenant, utilisez la classe comme ceci:

    [TestMethod]
    public void TestMethod1()
    {
        ParallelTesting.AsyncExecutionContext(TestContext.DataRow, (row)=>
            {
                //Test Logic goes here.
            }
        );
    }
    

Remarque: Vous devrez faire quelques bricolages avec des exceptions pour les faire bouillonner correctement (vous pouvez avoir une exception globale ici, vous aurez besoin de la première exception). Le temps affiché pour l'exécution de chaque test ne sera plus précis. Vous voudrez également nettoyer la classe ParallelTesting une fois la dernière ligne terminée.

Comment ça marche: La logique de test est enveloppée dans un lambda et passée à une classe statique qui exécutera la logique une fois pour chaque ligne de données de test lors de son premier appel (première ligne exécutée). Les appels successifs à la classe statique attendent simplement que la tâche de test pré-démarrée se termine.

De cette façon, chaque appel que le framework de test fait à TestMethod recueille simplement les résultats du test correspondant déjà exécuté.

Améliorations possibles:

  • Faites en sorte que AsyncExecutionContext prenne un paramètre maxSynchronousTasks.
  • Examinez comment le framework déplace des traces de pile complètes sur du code non managé pour voir si l'exception Task.Exception peut être transmise au framework de test de Visual Studio sans avoir à relancer et détruire la trace de pile.
1
N-ate