Partager via


Test unitaire d'une DLL Visual C++ pour les applications du Windows Store

Cette rubrique décrit une méthode de création de tests unitaires pour une DLL C++ pour les applications Windows Store à l'aide de Visual Studio 2012 Express pour Windows 8 et de l'infrastructure de test unitaire Microsoft pour C++. La DLL RooterLib 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é. La DLL peut ensuite être incluse dans une application Windows Store pour montrer à l'utilisateur les activités ludiques que l'on peut faire avec les mathématiques.

Notes

Les rubriques de cette section décrivent les fonctionnalités de Visual Studio 2012 Express pour Windows 8. Visual Studio Ultimate, VS Premium et VS Professional fournissent des fonctionnalités de test unitaire supplémentaires.

  • Dans VS Ultimate, VS Premium et VS Professional, vous pouvez utiliser une infrastructure de test unitaire tierce ou open source qui a créé un adaptateur complémentaire pour l'explorateur de tests Microsoft. Vous pouvez également analyser et afficher les informations de couverture du code pour les tests.

  • Dans VS Ultimate et VS Premium, vous pouvez exécuter vos tests après chaque génération.

Pour plus d'informations, consultez Vérification du code à l'aide de tests unitaires dans MSDN Library.

Cette rubrique montre comment utiliser le test unitaire comme première étape du développement. 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. 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.

Cette rubrique crée également une solution Visual Studio unique et des projets distincts pour les tests unitaires et la DLL que vous souhaitez tester. 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. Consultez Applications C++ existantes de tests unitaires avec Test Explorer pour obtenir des conseils sur la structure à utiliser.

Dans cette rubrique

Cette rubrique vous guide à travers les tâches suivantes :

Créer la solution et le projet de test unitaire

Vérifier l'exécution des tests dans l'explorateur de tests

Ajouter le projet DLL à la solution

Associer le projet de test au projet dll

Augmenter itérativement les tests et les faire réussir

Déboguer un test échoué

Refactoriser le code sans modifier les tests

Créer la solution et le projet de test unitaire

  1. Dans le menu Fichier, choisissez Nouveau, puis Nouveau projet.

  2. Dans la boîte de dialogue Nouveau projet, développez Installé, Visual C++, puis choisissez Windows Store. Choisissez ensuite Bibliothèque de tests unitaires (applications Windows Store) dans la liste des modèles de projet.

    Créer une bibliothèque de test unitaire C++

  3. Nommez le projet RooterLibTests, spécifiez l'emplacement, nommez la solution RooterLib et vérifiez que Créer le répertoire pour la solution est activé.

    Spécifier la solution, le nom du projet et l'emplacement

  4. Dans le nouveau projet, ouvrez unittest1.cpp.

    unittest1.cpp

    Prenez note de ce qui suit :

    • Chaque test est défini à l'aide de TEST_METHOD(YourTestName){...}.

      Vous ne devez pas écrire une signature de fonction classique. La signature est créée par la macro TEST_METHOD. La macro génère une fonction d'instance qui retourne void. Elle génère également une fonction statique qui retourne des informations sur la méthode de test. Ces informations permettent à l'explorateur de tests de rechercher la méthode.

    • Les méthodes de test sont regroupées en classes à l'aide de TEST_CLASS(YourClassName){...}.

      Lorsque les tests sont exécutés, une instance de chaque classe de test est créée. Les méthodes de test sont appelées dans un ordre non spécifié. Vous pouvez définir des méthodes spéciales qui sont appelées avant et après chaque module, classe ou méthode. Pour plus d'informations, consultez Utilisation de Microsoft.VisualStudio.TestTools.CppUnitTestFramework dans MSDN Library.

Vérifier l'exécution des tests dans l'explorateur de tests

  1. Insérez du code de test :

    TEST_METHOD(TestMethod1)
    {
        Assert::AreEqual(1,1);
    }
    

    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.

  2. Dans le menu Test, choisissez Exécuter, puis Exécuter tout.

    Le projet de test est généré et exécuté. La fenêtre de l'explorateur de tests s'affiche, et le test est répertorié sous Tests réussis. Le volet de résumé situé au bas de la fenêtre fournit des informations supplémentaires sur le test sélectionné.

    Explorateur de tests

Ajouter le projet DLL à la solution

  1. Dans l'explorateur de solutions, sélectionnez le nom de la solution. Dans le menu contextuel, sélectionnez Ajouter, puis Ajouter un nouveau projet.

    Créer le projet RooterLib

  2. Dans la boîte de dialogue Ajouter un nouveau projet, choisissez DLL (applications Windows Store).

  3. Ajoutez le code suivant au fichier RooterLib.h :

    // The following ifdef block is the standard way of creating macros which make exporting 
    // from a DLL simpler. All files within this DLL are compiled with the ROOTERLIB_EXPORTS
    // symbol defined on the command line. This symbol should not be defined on any project
    // that uses this DLL. This way any other project whose source files include this file see 
    // ROOTERLIB_API functions as being imported from a DLL, whereas this DLL sees symbols
    // defined with this macro as being exported.
    #ifdef ROOTERLIB_EXPORTS
    #define ROOTERLIB_API  __declspec(dllexport)
    #else
    #define ROOTERLIB_API __declspec(dllimport)
    #endif //ROOTERLIB_EXPORTS
    
    class ROOTERLIB_API CRooterLib {
    public:
        CRooterLib(void);
        double SquareRoot(double v);
    };
    

    Les commentaires expliquent le bloc ifdef non seulement au développeur de la DLL, mais à toute personne qui référence la DLL dans son projet. Vous pouvez ajouter le symbole ROOTERLIB_EXPORTS à la ligne de commande à l'aide des propriétés de projet de la DLL.

    La classe CRooterLib déclare un constructeur et la méthode d'estimation SqareRoot.

  4. Ajoutez le symbole ROOTERLIB_EXPORTS à la ligne de commande.

    1. Dans l'explorateur de solutions, sélectionnez le projet RooterLib, puis Propriétés dans le menu contextuel.

      Ajouter la définition d'un symbole de préprocesseur

    2. Dans la boîte de dialogue Pages de propriétés de RooterLib, développez Propriétés de configuration, C++, puis choisissez Préprocesseur.

    3. Choisissez <Modifier...> dans la liste Définitions de préprocesseur, puis ajoutez ROOTERLIB_EXPORTS dans la boîte de dialogue Définitions de préprocesseur.

  5. Ajoutez les implémentations minimales des fonctions déclarées. Ouvrez RooterLib.cpp et ajoutez le code suivant :

    // constructor
    CRooterLib::CRooterLib()
    {
    }
    
    // Find the square root of a number.
    double CRooterLib::SquareRoot(double v)
    {
        return 0.0;
    }
    

Associer le projet de test au projet dll

  1. Ajoutez RooterLib au projet RooterLibTests.

    1. Dans l'explorateur de solutions, sélectionnez le projet RooterLibTests, puis Références... dans le menu contextuel.

    2. Dans la boîte de dialogue Propriétés du projet RooterLib, développez Propriétés communes et choisissez Structure et références.

    3. Choisissez Ajouter une nouvelle référence...

    4. Dans la boîte de dialogue Ajouter une référence, développez Solution, puis choisissez Projets. Sélectionnez ensuite l'élément RouterLib.

  2. Ajoutez le fichier d'en-tête RooterLib dans unittest1.cpp.

    1. Ouvrez unittest1.cpp.

    2. Ajoutez le code suivant sous la ligne #include "CppUnitTest.h" :

      #include "..\RooterLib\RooterLib.h"
      
  3. Ajoutez un test qui utilise la fonction importée. Ajoutez le code suivant à unittest1.cpp :

    TEST_METHOD(BasicTest)
    {
        CRooterLib rooter;
        Assert::AreEqual(
            // Expected value:
            0.0, 
            // Actual value:
            rooter.SquareRoot(0.0), 
            // Tolerance:
            0.01,
            // Message:
            L"Basic test failed",
            // Line number - used if there is no PDB file:
            LINE_INFO());
    }
    
  4. Générez la solution.

    Le nouveau test s'affiche dans l'explorateur de tests dans le nœud Tests non exécutés.

  5. Dans l'explorateur de tests, sélectionnez Exécuter tout.

    Test de base réussi

Vous avez configuré le test et les projets de code, et vérifié qu'il est possible d'exécuter des tests qui exécutent des fonctions dans le projet de code. Vous pouvez maintenant commencer à écrire des tests et du code réels.

Augmenter itérativement les tests et les faire réussir

  1. Ajoutez un nouvel test :

    TEST_METHOD(RangeTest)
    {
        CRooterLib rooter;
        for (double v = 1e-6; v < 1e6; v = v * 3.2)
        {
            double expected = v;
            double actual = rooter.SquareRoot(v*v);
            double tolerance = expected/1000;
            Assert::AreEqual(expected, actual, tolerance);
        }
    };
    

    Conseil

    Nous vous recommandons de ne pas modifier les tests qui ont réussi. Ajoutez plutôt un nouveau test, mettez à jour le code afin que le test réussisse, puis ajoutez un autre test, et ainsi de suite.

    Lorsque les utilisateurs modifient leurs spécifications, désactivez les tests qui ne sont plus corrects. Écrivez de nouveaux tests et exécutez-les individuellement, de la même façon incrémentielle.

  2. Dans l'explorateur de tests, sélectionnez Exécuter tout.

  3. Le test échoue.

    RangeTest a échoué

    Conseil

    Vérifiez que chaque test échoue immédiatement après l'avoir écrit. Cela permet d'éviter l'erreur facile d'écrire un test qui n'échoue jamais.

  4. Améliorez le code testé afin que le nouveau test réussisse. Ajoutez le code suivant à RooterLib.cpp :

    #include <math.h>
    ...
    // Find the square root of a number.
    double CRooterLib::SquareRoot(double v)
    {
        double result = v;
        double diff = v;
        while (diff > result/1000)
        {
            double oldResult = result;
            result = result - (result*result - v)/(2*result);
            diff = abs (oldResult - result);
        }
        return result;
    }
    
  5. Générez la solution, puis sélectionnez Exécuter tout dans l'explorateur de tests.

    Les deux tests réussissent.

Conseil

Développez le code en ajoutant les tests individuellement. Vérifiez que tous les tests réussissent après chaque itération.

Déboguer un test échoué

  1. Ajoutez un autre test à unittest1.cpp :

    // Verify that negative inputs throw an exception.
     TEST_METHOD(NegativeRangeTest)
     {
       wchar_t message[200];
       CRooterLib rooter;
       for (double v = -0.1; v > -3.0; v = v - 0.5)
       {
         try 
         {
           // Should raise an exception:
           double result = rooter.SquareRoot(v);
    
           swprintf_s(message, L"No exception for input %g", v);
           Assert::Fail(message, LINE_INFO());
         }
         catch (std::out_of_range ex)
         {
           continue; // Correct exception.
         }
         catch (...)
         {
           swprintf_s(message, L"Incorrect exception for %g", v);
           Assert::Fail(message, LINE_INFO());
         }
       }
    };
    
  2. Dans l'explorateur de tests, sélectionnez Exécuter tout.

    Le test échoue. Sélectionnez le nom du test dans l'explorateur de tests. L'assertion échouée est mise en surbrillance. Le message d'échec est visible dans le volet de détails de l'explorateur de tests.

    Échec NegativeRangeTests

  3. Pour comprendre pourquoi le test échoue, parcourez la fonction :

    1. Définissez un point d'arrêt au début de la fonction SquareRoot.

    2. Dans le menu contextuel du test échoué, choisissez Déboguer les tests sélectionnés.

      Lorsque l'exécution s'arrête au point d'arrêt, parcourez le code.

    3. Ajoutez le code à RooterLib.cpp pour intercepter l'exception :

      #include <stdexcept>
      ...
      double CRooterLib::SquareRoot(double v)
      {
          //Validate the input parameter:
          if (v < 0.0) 
          {
            throw std::out_of_range("Can't do square roots of negatives");
          }
      ...
      
    1. Dans l'explorateur de tests, sélectionnez Exécuter tout pour tester la méthode corrigée et vérifier que vous n'avez pas créé une régression.

Tous les tests réussissent maintenant.

Tous les tests sont concluants

Refactoriser le code sans modifier les tests

  1. Simplifiez le calcul central dans la fonction SquareRoot :

    // 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 créé une régression.

    Conseil

    Un ensemble stable de tests unitaires corrects est l'assurance que vous n'avez pas créé de bogues lors de la modification du code.

    Conservez les modifications de refactorisation séparées des autres modifications.