Pour afficher l’article en anglais, activez la case d’option Anglais. Vous pouvez aussi afficher la version anglaise dans une fenêtre contextuelle en faisant glisser le pointeur de la souris sur le texte.
Traduction
Anglais
Nous recommandons d’utiliser Visual Studio 2017

Tests unitaires du code natif avec Test Explorer

Dans Visual Studio, vous pouvez créer des tests unitaires pour le code non managé écrit en C++. Code non managé est parfois appelé code natif.

La procédure suivante contient les informations nécessaires pour vous permettre de démarrer. Les sections suivantes fournissent une procédure pas - à - pas qui décrit les étapes en détail.

Pour écrire des tests unitaires pour une DLL de code non managé

  1. Utilisez le modèle projet de test natif pour créer un projet Visual Studio distinct pour vos tests.

    Le projet contient du code de test d'exemple.

  2. Rendez la DLL accessible au projet de test :

    • #include un fichier .h qui contient des déclarations de fonctions extérieurement accessibles à partir de la DLL.

      Le fichier .h doit contenir des déclarations de fonction marquées avec _declspec(dllimport). Sinon, vous pouvez exporter des méthodes à l'aide d'un fichier de DEF. Pour plus d'informations, consultez Importing and Exporting from a DLL.

      Vos tests unitaires peuvent accéder uniquement aux fonctions exportées à partir de la DLL testée.

    • Ajoutez le projet de DLL aux références du projet de test :

      Dans Propriétés du projet de test, développez Propriétés communes, infrastructure et références, puis choisissez Ajouter une référence.

  3. Dans le projet de test, créez les classes et les méthodes de test en utilisant des macros TEST et la classe Asert de la manière suivante :

    #include "stdafx.h"
    #include <CppUnitTest.h>
    #include "..\MyProjectUnderTest\MyCodeUnderTest.h"
    using namespace Microsoft::VisualStudio::CppUnitTestFramework;
    TEST_CLASS(TestClassName)
    {
    public:
      TEST_METHOD(TestMethodName)
      {
        // Run a function under test here.
        Assert::AreEqual(expectedValue, actualValue, L"message", LINE_INFO());
      }
    }
    
    • Assert contient plusieurs fonctions statiques que vous pouvez utiliser pour vérifier le résultat d'un test.

    • Le paramètre LINE_INFO() est optionnel. Dans les cas où il n'existe aucun fichier PDB, il permet au testeur d´identifier l'emplacement d'un échec.

    • Vous pouvez également écrire des méthodes d'installation et de nettoyage de test. Pour plus d'informations, ouvrez la définition de la macro TEST_METHOD, puis lisez les commentaires dans CppUnitTest.h

    • Vous ne pouvez pas imbriquer des classes de test.

  4. Explorateur de tests à utiliser pour exécuter les tests :

    1. Dans le menu Affichage, choisissez Autres fenêtres, Explorateur de tests.

    2. Générez la solution Visual Studio.

    3. Dans l'Explorateur de tests, choisissez Exécuter tout.

    4. Pour examiner un test plus en détail dans l'explorateur de tests :

      1. Sélectionnez le nom du test pour afficher plus de détails tels qu'un message et une trace de la pile d'échec.

      2. Ouvrez le nom du test (par exemple en double-cliquant) pour accéder à l'emplacement d'échec ou au code de test.

      3. Dans le menu contextuel pour un test, choisissez Déboguer le test sélectionné pour exécuter le test dans le débogueur.

Vous pouvez adapter cette procédure pas - à - pas pour développer votre propre DLL. Les étapes principales sont les suivantes :

  1. créez un projet de test natif. Les tests sont créés dans un projet séparé de la DLL que vous développez.

  2. Créer un project de DLL. Cette procédure pas - à - pas crée une DLL, mais la procédure pour tester une DLL existante est similaire.

  3. Rendez les fonctions DLL visibles par les tests.

  4. Augmentez de manière itérative des tests. Nous recommandons le cycle « red-green-refactor », dans lequel le développement du code est mené par les tests.

  5. Tests ont échoué le Debug. Vous pouvez exécuter des tests en mode débogage.

  6. Refactoriser tout en conservant les tests pas affectés. Refactoriser signifie améliorer la structure du code sans modifier son comportement externe. Vous pouvez le faire pour améliorer les performances, extensibilité, ou la lisibilité du code. Étant donné que l'objectif n'est pas de modifier le comportement, vous ne modifiez pas les tests en apportant une modification de refactorisation du code. L'utilisation de tests permet de vérifier que vous n´introduisez pas de bogues lorsque vous refactorisez. Vous pouvez donc effectuer de telles modifications avec beaucoup plus de confiance que si vous n'aviez pas de tests.

  7. Vérifiez la couverture. Les tests unitaires sont plus utiles lorsqu'ils utilisent plus de votre code. Vous pouvez découvrir quelles parties de votre code ont été utilisées par les tests.

  8. Unités à isoler des ressources externes. En général, une DLL dépend d'autres parties du système que vous développez, tel que d'autres DLL, les bases de données, ou des sous-systèmes distants. Il est utile de tester chaque unité de manière isolée de ses dépendances. Les composants externes peuvent effectuer le test lentement. Pendant le développement, les autres composants peuvent ne pas être complets.

Créer un projet de test unitaire natif

  1. Dans le menu Fichier, cliquez sur Nouveau, Projet.

    Dans la boîte de dialogue, développez Installé, Modèles, Visual C++, Test.

    Sélectionnez le modèle projet de test natif.

    Dans cette procédure pas - à - pas, le projet de test est nommé NativeRooterTest.

    Création d'un projet de test unitaire C++
  2. Dans le nouveau projet, examinez unittest1.cpp

    Projet de test avec TEST_CLASS et TEST_METHOD

    Notez que :

    • Chaque test est défini en utilisant 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. Il 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 groupées en classes en utilisant 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 Organiser des tests C++.

  3. Vérifiez que les tests sont lancés dans l'explorateur de tests :

    1. Insérez un 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 , Tous les tests.

      Les tests sont construits et exécutés.

      L'explorateur de tests s'affiche.

      Le test s'affiche sous Tests réussis.

      Explorateur de tests unitaires avec un test réussi

Créez un projet de DLL non managée

  1. Créez un projet Visual C++ à l'aide du modèle Projet Win32.

    Dans cette procédure pas - à - pas, le projet est nommé RootFinder.

    Création d’un projet Win32 C++
  2. Sélectionnez DLL et symboles d'exportation dans l'Assistant Application Win32.

    L'option Exporte les symboles génère une macro pratique que vous pouvez utiliser pour déclarer des méthodes exportées.

    Assistant de projet C++ défini pour les symboles DLL et d'exportation
  3. Déclarez une fonction exportée dans le fichier .h principal :

    Nouveau projet de code de la DLL et fichier .h avec macros API

    Le déclarateur __declspec(dllexport) rend le public et les membres protégés de la classe visibles à l'extérieur de la DLL. Pour plus d'informations, consultez Utilisation de dllimport et dllexport dans les classes C++.

  4. Dans le fichier .cpp principal, ajoutez un corps minimal pour la fonction :

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

Couplez le projet de test dans le projet de DLL

  1. Ajoutez le projet de DLL aux références de projet du projet de test :

    1. Ouvrez les propriétés du projet de test et choisissez Propriétés communes, infrastructure et références.

      Propriétés du projet C++ - Structure et références
    2. Choisissez Ajouter une nouvelle référence.

      Dans la boîte de dialogue Ajouter une référence, sélectionnez le projet de DLL et choisissez Ajouter.

      Propriétés du projet C++ - Ajouter une nouvelle référence
  2. Dans le fichier .cpp principal de test unitaire, incluez le fichier .h du code de la DLL :

    #include "..\RootFinder\RootFinder.h"
    
  3. Ajoutez un test de base qui utilise la fonction exportée :

    TEST_METHOD(BasicTest)
    {
    CRootFinder 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.

  5. Dans l'Explorateur de tests, choisissez Exécuter tout.

    Explorateur de tests unitaires - Test de base réussi

Vous avez installé le test et les projets de code, et vérifié que vous pouvez exécuter des tests qui exécutent des fonctions dans le projet de code. Vous pouvez maintenant commencer à écrire de vrais tests et du code.

De manière itérative augmentez les tests et faites les passer

  1. Ajoutez un nouveau test :

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

    Nous vous conseillons de ne pas modifier les tests qui ont réussi. À la place, ajoutez 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 appropriés. Entrez les nouveaux tests et faites-les s´exécuter un par un, de la même façon incrémentielle.

  2. Générez la solution, puis dans l'explorateur de tests, sélectionnez Exécuter tout.

    Les nouveaux tests échouent.

    RangeTest a échoué
    Conseil Conseil

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

  3. Améliorer le code sous test afin que le test réussisse :

    #include <math.h>
    ...
    double CRootFinder::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;
    }
    
  4. Générez la solution puis dans l'explorateur de tests, sélectionnez Exécuter tout.

    Les deux tests passent.

    Explorateur de tests unitaires - Réussite du test de plage
    Conseil Conseil

    Développez le code en ajoutant des tests un par un. Assurez -vous que tous les tests réussissent après chaque itération.

Déboguez un test qui a échoué

  1. Ajoutez un autre test :

    
    #include <stdexcept>
    ...
    // Verify that negative inputs throw an exception.
    TEST_METHOD(NegativeRangeTest)
    {
      wchar_t message[200];
      CRootFinder rooter;
      for (double v = -0.1; v > -3.0; v = v - 0.5)
      {
        try 
        {
          // Should raise an exception:
          double result = rooter.SquareRoot(v);
    
          _swprintf(message, L"No exception for input %g", v);
          Assert::Fail(message, LINE_INFO());
        }
        catch (std::out_of_range ex)
        {
          continue; // Correct exception.
        }
        catch (...)
        {
          _swprintf(message, L"Incorrect exception for %g", v);
          Assert::Fail(message, LINE_INFO());
        }
      }
    }
    
  2. Générez la solution et sélectionnez Exécuter tout.

  3. Ouvrez (ou double-cliquez) sur le test qui a échoué.

    L'assertion erronée est mise en surbrillance. Le message d'erreur est visible dans le volet d'informations de l'explorateur de tests.

    Échec NegativeRangeTests
  4. Pour voir pourquoi le test échoue, parcourir la fonction :

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

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

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

  5. Insérez le code dans la fonction que vous développez :

    
    #include <stdexcept>
    ...
    double CRootFinder::SquareRoot(double v)
    {
        // Validate parameter:
        if (v < 0.0) 
        {
          throw std::out_of_range("Can't do square roots of negatives");
        }
    
    
  6. Tous les tests réussissent maintenant.

    Tous les tests sont concluants

Refactorisez le code sans modifier de tests

  1. Simplifiez le calcul central dans la fonction de SquareRoot :

    // old code:
    //   result = result - (result*result - v)/(2*result);
    // new code:
         result = (result + v/result)/2.0;
    
    
  2. Générez la solution et sélectionnez Exécuter tout, pour vous assurer que vous n'avez pas introduit une erreur.

    Conseil Conseil

    Un bon jeu de tests unitaires donne confiance que vous n'avez pas introduit des bogues lorsque vous modifiez le code.

    Conservez la refactorisation séparées d'autres modifications.

  • Isolement La plupart des DLL dépendent d'autres sous-systèmes tels que des bases de données et d'autres DLL. Ces autres composants sont souvent développés en parallèle. Pour permettre au test unitaire de s' exécuter alors que les autres composants ne sont pas encore disponibles, vous devez substituer la simulation ou

  • Tests de vérification de build (BVT). Vous pouvez avoir des tests exécutés sur le serveur de builds de votre équipe à des intervalles définis. Cela garantit que les bogues ne sont pas introduits lorsque le travail de plusieurs membres de l'équipe est intégré.

  • Tests de Checkin. Vous pouvez exiger que certains tests soient exécutés avant que chaque membre de l'équipe contrôle le code dans le contrôle de code source. En général c'est un sous-ensemble du jeu complet de tests de vérification de build.

    Vous pouvez également avoir besoin d'un niveau minimum de couverture du code.

Afficher: