Tests unitaires du code Visual C#Unit testing Visual C# code

Cet article décrit une méthode permettant de créer des tests unitaires pour une classe Visual C# dans une application UWP.This article describes one way to create unit tests for a Visual C# class in a UWP app. La classe Rooter illustre de vagues souvenirs de la théorie de limite du calcul en implémentant une fonction qui calcule une estimation de la racine carrée d'un nombre donné.The Rooter class demonstrates vague memories of limit theory from calculus by implementing a function that calculates an estimate of the square root of a given number. L'application Maths peut ensuite utiliser cette fonction pour montrer à l'utilisateur les activités ludiques que l'on peut faire avec les mathématiques.The Maths app can then use this function to show a user the fun things that can be done with math.

Cet article explique comment utiliser le test unitaire comme première étape du développement.This article demonstrates how to use unit testing as the first step in development. Dans cette approche, vous écrivez d'abord une méthode de test qui vérifie un comportement spécifique dans le système que vous testez, puis vous écrivez le code qui réussit le test.In this approach, you first write a test method that verifies a specific behavior in the system that you are testing and then you write the code that passes the test. En modifiant l'ordre des procédures suivantes, vous pouvez inverser cette stratégie de manière à écrire d'abord le code que vous souhaitez tester, puis à écrire les tests unitaires.By making changes in the order of the following procedures, you can reverse this strategy to first write the code that you want to test and then write the unit tests.

Cet article crée également une solution Visual Studio unique et des projets distincts pour les tests unitaires et la DLL que vous souhaitez tester.This article also creates a single Visual Studio solution and separate projects for the unit tests and the DLL that you want to test. Vous pouvez également inclure les tests unitaires directement dans le projet DLL, ou vous pouvez créer des solutions distinctes pour les tests unitaires et la DLL.You can also include the unit tests directly in the DLL project, or you can create separate solutions for the unit tests and the DLL.

Créer la solution et le projet de test unitaireCreate the solution and the unit test project

  1. Dans le menu Fichier, choisissez Nouveau > Projet.On the File menu, choose New > Project.

  2. Dans la boîte de dialogue Nouveau projet, développez Installé > Visual C#, puis choisissez Windows universel.In the New Project dialog box, expand Installed > Visual C# and choose Windows Universal. Choisissez ensuite Application vide dans la liste de modèles de projet.Then choose Blank App from the list of project templates.

  3. Nommez le projet Maths et vérifiez que Créer le répertoire pour la solution est sélectionné.Name the project Maths and make sure Create directory for solution is selected.

  4. Dans l’Explorateur de solutions, choisissez le nom de la solution, Ajouter dans le menu contextuel, puis Nouveau projet.In Solution Explorer, choose the solution name, choose Add from the shortcut menu, and then choose New Project.

  5. Dans la boîte de dialogue Nouveau projet, développez Installé, Visual C#, puis choisissez Windows universel.In the New Project dialog box, expand Installed, then expand Visual C# and choose Windows Universal. Choisissez ensuite Application de tests unitaires (Windows universel) dans la liste des modèles de projet.Then choose Unit Test App (Universal Windows) from the list of project templates.

  6. Ouvrez UnitTest1.cs dans l'éditeur Visual Studio.Open UnitTest1.cs in the Visual Studio editor.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Maths;
    
    namespace RooterTests
    {
        [TestClass]
        public class UnitTest1
    
            [TestMethod]
            public void TestMethod1()
            {
    
            }
    

    Prenez note de ce qui suit :Note that:

    • Chaque test est défini à l’aide de l’attribut TestMethodAttribute.Each test is defined by using the TestMethodAttribute attribute. Une méthode de test doit retourner void et ne contient pas de paramètres.A test method must return void and can't have any parameters.

    • Les méthodes de test doivent figurer dans une classe décorée avec l'attribut TestClassAttribute.Test methods must be in a class decorated with the TestClassAttribute attribute.

      Lorsque les tests sont exécutés, une instance de chaque classe de test est créée.When the tests are run, an instance of each test class is created. Les méthodes de test sont appelées dans un ordre non défini.The test methods are called in an unspecified order.

    • Vous pouvez définir des méthodes spéciales qui sont appelées avant et après chaque module, classe ou méthode.You can define special methods that are invoked before and after each module, class, or method. Pour plus d’informations, consultez Utiliser le framework MSTest dans les tests unitaires.For more information, see Use the MSTest framework in unit tests.

Vérifier l'exécution des tests dans l'explorateur de testsVerify that the tests run in Test Explorer

  1. Insérez du code de test dans TestMethod1 dans le fichier TestMethod1.cs :Insert some test code in TestMethod1 in the UnitTest1.cs file:

    [TestMethod]
    public void TestMethod1()
    {
        Assert.AreEqual(0, 0);
    }
    

    Notez que la classe Assert fournit plusieurs méthodes statiques que vous pouvez utiliser pour vérifier les résultats dans les méthodes de test.Notice that the Assert class provides several static methods that you can use to verify results in test methods.

  2. Dans le menu Test, choisissez Exécuter, puis Exécuter tout.On the Test menu, choose Run and then choose Run All.

    Le projet de test est généré et exécuté.The test project builds and runs. La fenêtre Explorateur de tests s’affiche, et le test est répertorié sous Tests réussis.The Test Explorer window appears, and the test is listed under Passed Tests. Le volet Résumé situé au bas de la fenêtre fournit des informations supplémentaires sur le test sélectionné.The Summary pane at the bottom of the window provides additional details about the selected test.

    Explorateur de tests

Ajouter la classe Rooter au projet MathsAdd the Rooter class to the Maths project

  1. Dans l’Explorateur de solutions, sélectionnez le nom de projet Maths.In Solution Explorer, choose the Maths project name. Dans le menu contextuel, choisissez Ajouter, puis Classe.From the shortcut menu, choose Add, and then Class.

  2. Nommez le fichier de classe Rooter.cs.Name the class file Rooter.cs.

  3. Ajoutez le code suivant au fichier Rooter.cs de la classe Rooter :Add the following code to the Rooter class Rooter.cs file:

    public Rooter()
    {
    }
    
    // estimate the square root of a number
    public double SquareRoot(double x)
    {
        return 0.0;
    }
    

    La classe Rooter déclare un constructeur et la méthode d'estimation SquareRoot.The Rooter class declares a constructor and the SquareRoot estimator method.

  4. La méthode SquareRoot n'est qu'une implémentation minimale, qui est suffisante pour tester la structure de base de la configuration de test.The SquareRoot method is only a minimal implementation, just enough to test the basic structure of the testing setup.

Associer le projet de test au projet d'applicationCouple the test project to the app project

  1. Ajoutez une référence de l'application Maths au projet RooterTests.Add a reference to the Maths app to the RooterTests project.

    1. Dans l’Explorateur de solutions, sélectionnez le projet RooterTests, puis Ajouter une référence dans le menu contextuel.In Solution Explorer, choose the RooterTests project and then choose Add Reference on the shortcut menu.

    2. Dans la boîte de dialogue Ajouter une référence - RooterTests, développez Solution, puis choisissez Projets.In the Add Reference - RooterTests dialog box, expand Solution and choose Projects. Sélectionnez ensuite l’élément Maths.Then select the Maths item.

      Ajouter une référence au projet Maths

  2. Ajoutez une instruction using au fichier UnitTest1.cs :Add a using statement to the UnitTest1.cs file:

    1. Ouvrez UnitTest1.cs.Open UnitTest1.cs.

    2. Ajoutez le code suivant sous la ligne using Microsoft.VisualStudio.TestTools.UnitTesting; :Add this code below the using Microsoft.VisualStudio.TestTools.UnitTesting; line:

      using Maths;
      
  3. Ajoutez un test qui utilise la fonction Rooter.Add a test that uses the Rooter function. Ajoutez le code suivant à UnitTest1.cs :Add the following code to UnitTest1.cs:

    [TestMethod]
    public void BasicTest()
    {
        Maths.Rooter rooter = new Rooter();
        double expected = 0.0;
        double actual = rooter.SquareRoot(expected * expected);
        double tolerance = .001;
        Assert.AreEqual(expected, actual, tolerance);
    }
    
  4. Générez la solution.Build the solution.

    Le nouveau test s’affiche dans l’Explorateur de tests, dans le nœud Tests non exécutés.The new test appears in Test Explorer in the Not Run Tests node.

  5. Dans l’Explorateur de tests, choisissez Exécuter tout.In Test Explorer, choose Run All.

    Test de base réussi

Vous avez configuré le test et les projets de code, et vérifié que vous pouviez exécuter des tests exécutant les fonctions du projet de code.You have set up the test and the code projects, and verified that you can run tests that run functions in the code project. Maintenant, vous pouvez commencer à écrire le code et les tests réels.Now you can begin to write real tests and code.

Augmenter itérativement les tests et les faire réussirIteratively augment the tests and make them pass

  1. Ajoutez un nouveau test :Add a new test:

    [TestMethod]
    public void RangeTest()
    {
        Rooter rooter = new Rooter();
        for (double v = 1e-6; v < 1e6; v = v * 3.2)
        {
            double expected = v;
            double actual = rooter.SquareRoot(v*v);
            double tolerance = ToleranceHelper(expected);
            Assert.AreEqual(expected, actual, tolerance);
        }
    }
    

    Tip

    Nous vous recommandons de ne pas modifier les tests ayant réussi.We recommend that you do not change tests that have passed. Ajoutez à la place un nouveau test, mettez à jour le code afin que le test réussisse, puis ajoutez un autre test, et ainsi de suite.Instead, add a new test, update the code so that the test passes, and then add another test, and so on.

    Lorsque vos utilisateurs modifient leurs spécifications, désactivez les tests qui ne sont plus corrects.When your users change their requirements, disable the tests that are no longer correct. Écrivez de nouveaux tests et utilisez-les l'un après l'autre, de la même façon incrémentielle.Write new tests and make them work one at a time, in the same incremental manner.

  2. Dans l’Explorateur de tests, choisissez Exécuter tout.In Test Explorer, choose Run All.

  3. Le test échoue.The test fails.

    RangeTest a échoué

    Tip

    Immédiatement après l'avoir écrit, vérifiez que chaque test échoue.Immediately after you have written it, verify that each test fails. Vous évitez ainsi de commettre l'erreur d'écrire un test qui n'échoue jamais.This helps you avoid the easy mistake of writing a test that never fails.

  4. Améliorez le code testé afin que le nouveau test réussisse.Enhance the code under test so that the new test passes. Remplacez la fonction SquareRoot dans Rooter.cs par ce qui suit :Change the SquareRoot function in Rooter.cs to this:

    public double SquareRoot(double x)
    {
        double estimate = x;
        double diff = x;
        while (diff > estimate / 1000)
        {
            double previousEstimate = estimate;
            estimate = estimate - (estimate * estimate - x) / (2 * estimate);
            diff = Math.Abs(previousEstimate - estimate);
        }
        return estimate;
    }
    
  5. Générez la solution, puis, dans l’Explorateur de tests, choisissez Exécuter tout.Build the solution and then in Test Explorer, choose Run All.

    Les trois tests réussissent maintenant.All three tests now pass.

Tip

Développez le code en ajoutant les tests individuellement.Develop code by adding tests one at a time. Assurez-vous que tous les tests réussissent après chaque itération.Make sure that all the tests pass after each iteration.

Déboguer un test ayant échouéDebug a failing test

  1. Ajoutez un autre test à UnitTest1.cs :Add another test to UnitTest1.cs:

    // Verify that negative inputs throw an exception.
    [TestMethod]
    public void NegativeRangeTest()
    {
        string message;
        Rooter rooter = new Rooter();
        for (double v = -0.1; v > -3.0; v = v - 0.5)
        {
            try
            {
                // Should raise an exception:
                double actual = rooter.SquareRoot(v);
    
                message = String.Format("No exception for input {0}", v);
                Assert.Fail(message);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                continue; // Correct exception.
            }
            catch (Exception e)
            {
                message = String.Format("Incorrect exception for {0}", v);
                Assert.Fail(message);
            }
        }
    }
    
  2. Dans l’Explorateur de tests, choisissez Exécuter tout.In Test Explorer, choose Run All.

    Le test échoue.The test fails. Sélectionnez le nom du test dans l’Explorateur de tests.Choose the test name in Test Explorer. L'échec d'assertion est mis en surbrillance.The failed assertion is highlighted. Le message d’échec est visible dans le volet de détails de l’Explorateur de tests.The failure message is visible in the detail pane of Test Explorer.

    NegativeRangeTests, échec

  3. Pour voir pourquoi le test échoue, parcourez la fonction :To see why the test fails, step through the function:

    1. Définissez un point d'arrêt au début de la fonction SquareRoot.Set a breakpoint at the start of the SquareRoot function.

    2. Dans le menu contextuel du test ayant échoué, choisissez Déboguer les tests sélectionnés.On the shortcut menu of the failed test, choose Debug Selected Tests.

      Lorsque l'exécution s'arrête au point d'arrêt, parcourez le code.When the run stops at the breakpoint, step through the code.

    3. Ajoutez le code à la méthode Rooter pour intercepter l'exception :Add code to the Rooter method to catch the exception:

      public double SquareRoot(double x)
      {
          if (x < 0.0)
          {
              throw new ArgumentOutOfRangeException();
      }
      
  4. Dans l’Explorateur de tests, choisissez Exécuter tout pour tester la méthode corrigée et vérifier que vous n’avez pas introduit une régression.In Test Explorer, choose Run All to test the corrected method and make sure that you haven't introduced a regression.

Toutes les tests réussissent maintenant.All tests now pass.

Tous les tests sont concluants

Refactoriser le codeRefactor the code

Simplifier le calcul central de la fonction SquareRoot :Simplify the central calculation in the SquareRoot function.

  1. Modifiez l'implémentation du résultat.Change the result implementation

    // old code
    //result = result - (result*result - v)/(2*result);
    // new code
    result = (result + v/result) / 2.0;
    
  2. Choisissez Exécuter tout pour tester la méthode refactorisée et vérifier que vous n’avez pas introduit une régression.Choose Run All to test the refactored method and make sure that you haven't introduced a regression.

Tip

Un ensemble stable de tests unitaires corrects est l'assurance que vous n'avez pas créé de bogues lors de la modification du code.A stable set of good unit tests gives confidence that you have not introduced bugs when you change the code.

Refactoriser le code de test pour supprimer le code dupliqué.Refactor the test code to eliminate duplicated code.

Notez que la méthode RangeTest code en dur le dénominateur de la variable tolerance transférée dans la méthode Assert.Note that the RangeTest method hard codes the denominator of the tolerance variable that is passed to the Assert method. Si vous prévoyez d'ajouter des tests supplémentaires qui utilisent le même calcul de tolérance, l'utilisation d'une valeur codée en dur dans plusieurs emplacements peut provoquer des erreurs.If you plan to add additional tests that use the same tolerance calculation, the use of a hard-coded value in multiple locations could lead to errors.

  1. Ajoutez une méthode privée à la classe Unit1Test pour calculer la valeur de tolérance, puis appelez cette méthode à la place.Add a private method to the Unit1Test class to calculate the tolerance value, and then call that method instead.

    private double ToleranceHelper(double expected)
    {
        return expected / 1000;
    }
    
    ...
    
    [TestMethod]
    public void RangeTest()
    {
        ...
        // old code
        // double tolerance = expected/1000;
        // new code
        double tolerance = ToleranceHelper(expected);
        Assert.AreEqual(expected, actual, tolerance);
    }
    ...
    
  2. Choisissez Exécuter tout pour tester la méthode refactorisée et vérifier que vous n’avez pas créé une erreur.Choose Run All to test the refactored method and make sure that you haven't introduced an error.

Note

Si vous ajoutez une méthode d’assistance à une classe de test que vous ne souhaitez pas voir apparaître dans l’Explorateur de tests, n’ajoutez pas l’attribut TestMethodAttribute à la méthode.If you add a helper method to a test class that you don't want to appear in Test Explorer, do not add the TestMethodAttribute attribute to the method.