Komponententest einer Visual C++-DLL für Windows Store-Apps

In diesem Thema wird beschrieben, wie Sie mithilfe von Visual Studio 2012 Express für Windows 8 und dem Microsoft-Komponententest-Framework Komponententests für eine C++-DLL für Windows Store-Apps erstellen. Die DLL "RooterLib" implementiert eine Funktion zum näherungsweisen Berechnen der Quadratwurzel einer vorgegebenen Zahl, und zwar in einer Weise, die entfernt an Grenzwertberechnungen in der Analysis erinnert. Die DLL kann dann in einer Windows Store-App verwendet werden, um dem Benutzer zu zeigen, wie interessant und unterhaltsam Mathematik sein kann.

Hinweis

In den Themen dieses Abschnitts werden die Funktionen von Visual Studio Express 2012 für Windows 8 beschrieben. Visual Studio Ultimate, VS Premium und VS Professional stellen zusätzliche Funktionen für Komponententests bereit.

  • In VS Ultimate, VS Premium und VS Professional können Sie ein beliebiges Drittanbieter- und Open Source-Komponententest-Framework verwenden, mit dem ein Add-On-Adapter für den Microsoft-Test-Explorer erstellt wurde. Sie können auch Codeabdeckungsinformationen für die Tests analysieren und anzeigen.

  • In VS Ultimate und VS Premium können Sie die Tests nach jedem Build ausführen.

Weitere Informationen finden Sie unter Überprüfen von Code mithilfe von Komponententests in der MSDN Library.

In diesem Thema wird gezeigt, wie Komponententests als erster Schritt in der Entwicklung verwendet werden. Bei dieser Vorgehensweise schreiben Sie zuerst eine Testmethode, die ein bestimmtes Verhalten in Ihrem Testsystem überprüft. Anschließend schreiben Sie den Code, der im Test erfolgreich ist. Durch das Ändern der Reihenfolge bei den folgenden Prozeduren, können Sie diese Strategie umkehren und zuerst den zu testenden Code und anschließend die Komponententests schreiben.

In diesem Thema werden auch eine einzelne Visual Studio-Projektmappe und separate Projekte für die zu testenden Komponententests und DLLs erstellt. Sie können die Komponententests auch direkt in das DLL-Projekt einfügen, oder Sie können separate Lösungen für die Komponententests und die DLL erstellen. Weitere Tipps zur Verwendung der richtigen Struktur finden Sie unter Ausführen von Komponententests für vorhandene C++-Anwendungen mit dem Test-Explorer.

In diesem Thema

Dieses Thema enthält folgende Aufgaben:

Erstellen Sie die Projektmappe und das Komponententestprojekt.

Stellen Sie sicher, dass die Tests im Test-Explorer ausgeführt werden.

Fügen Sie der Projektmappe das DLL-Projekt hinzu.

Verknüpfen Sie das Testprojekt mit dem DLL-Projekt.

Steigern Sie die Tests iterativ, und schließen Sie sie erfolgreich ab.

Debuggen Sie einen fehlgeschlagenen Test.

Gestalten Sie den Code um, ohne die Tests zu ändern.

Erstellen Sie die Projektmappe und das Komponententestprojekt.

  1. Wählen Sie im Menü Datei die Option Neu aus, und klicken Sie dann auf Neues Projekt.

  2. Erweitern Sie im Dialogfeld "Neues Projekt" den Eintrag Installiert, erweitern Sie Visual C++, und wählen Sie Windows Store aus. Wählen Sie dann Komponententestbibliothek (Windows Store-Apps) aus der Liste der Projektvorlagen aus.

    C++-Komponententestbibliothek Erstellen

  3. Nennen Sie das Projekt RooterLibTests, und geben Sie den Speicherort an. Nennen Sie die Projektmappe RooterLib, und stellen Sie sicher, dass Projektmappenverzeichnis erstellen aktiviert ist.

    Name und Speicherort für Projektmappe und Projekt angeben

  4. Öffnen Sie im neuen Projekt unittest1.cpp.

    unittest1.cpp

    Hinweis:

    • Jeder Test wird durch die Verwendung von TEST_METHOD(YourTestName){...} definiert.

      Sie müssen keine herkömmliche Funktionssignatur schreiben. Die Signatur wird durch das Makro TEST_METHOD erstellt. Das Makro generiert eine Instanzfunktion, die den Wert "void" zurückgibt. Es generiert ebenfalls eine statische Funktion, die Informationen über die Testmethode zurückgibt. Diese Informationen ermöglichen dem Test-Explorer, die Methode zu suchen.

    • Die Testmethoden werden durch die Verwendung von TEST_CLASS(YourClassName){...} in Klassen gruppiert.

      Wenn die Tests ausgeführt werden, wird eine Instanz jeder Testklasse erstellt. Die Testmethoden werden in einer nicht angegebenen Reihenfolge aufgerufen. Sie können spezielle Methoden definieren, die vor und nach jedem Modul, jeder Klasse oder Methode aufgerufen werden. Weitere Informationen finden Sie unter Verwenden von Microsoft.VisualStudio.TestTools.CppUnitTestFramework in der MSDN Library.

Stellen Sie sicher, dass die Tests im Test-Explorer ausgeführt werden.

  1. Fügen Sie einen Testcode ein:

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

    Die Assert-Klasse stellt mehrere statische Methoden bereit, um die Ergebnisse in den Testmethoden zu überprüfen.

  2. Wählen Sie im Menü Test die Option Ausführen und dann Alle ausführen aus.

    Das Testprojekt wird erstellt und ausgeführt. Das Test-Explorer-Fenster wird angezeigt, und der Test wird unter Bestandene Tests aufgeführt. Unten im Fenster im Bereich "Zusammenfassung" werden weitere Informationen über den ausgewählten Test angezeigt.

    Test-Explorer

Fügen Sie der Projektmappe das DLL-Projekt hinzu.

  1. Wählen Sie im Projektmappen-Explorer den Projektmappennamen aus. Wählen Sie im Kontextmenü den Befehl Hinzufügen und dann Neues Element hinzufügen aus.

    RooterLib-Projekt erstellen

  2. Wählen Sie im Dialogfeld Neues Projekt hinzufügen die Option DLL (Windows Store-Apps) aus.

  3. Fügen Sie der RooterLib.h-Datei den folgenden Code hinzu:

    // 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);
    };
    

    In den Kommentaren wird sowohl dem DLL-Entwickler als auch allen Benutzern, die in ihren Projekten auf die DLL verweisen, die ifdef-Sperre erläutert. Sie können das ROOTERLIB_EXPORTS-Symbol zur Befehlszeile hinzufügen, indem Sie die Projekteigenschaften der DLL verwenden.

    Die CRooterLib-Klasse deklariert einen Konstruktor und die SqareRoot-Abschätzermethode.

  4. Fügen Sie das ROOTERLIB_EXPORTS-Symbol zur Befehlszeile hinzu.

    1. Wählen Sie im Projektmappen-Explorer das Projekt RooterLib aus, und wählen Sie anschließend im Kontextmenü Eigenschaften aus.

      Präprozessorsymboldefinition hinzufügen

    2. Erweitern Sie im Dialogfeld Eigenschaftenseite "RooterLib" den Eintrag C++, und wählen Sie Präprozessor aus.

    3. Wählen Sie <Bearbeiten...> aus der Liste Präprozessordefinitionen aus, und fügen Sie dann ROOTERLIB_EXPORTS im Dialogfeld "Präprozessordefinitionen" hinzu.

  5. Fügen Sie von den deklarierten Funktionen minimale Implementierungen hinzu. Öffnen Sie RooterLib.cpp, und fügen Sie den folgenden Code hinzu:

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

Verknüpfen Sie das Testprojekt mit dem DLL-Projekt.

  1. Fügen Sie RooterLib zu dem RooterLibTests-Projekt hinzu.

    1. Wählen Sie im Projektmappen-Explorer das Projekt RooterLibTests aus, und wählen Sie dann im Kontextmenü die Option Verweis hinzufügen aus.

    2. Erweitern Sie im Dialogfeld "Projekteigenschaften "RooterLib"" den Eintrag Allgemeine Eigenschaften, und wählen Sie Framework und Verweise aus.

    3. Wählen Sie Neuen Verweis hinzufügen... aus.

    4. Erweitern Sie im Dialogfeld Verweis hinzufügen den Eintrag Projektmappe, und wählen Sie dann Projekte aus. Wählen Sie dann das RouterLib-Element aus.

  2. Schließen Sie die RooterLib-Headerdatei in unittest1.cpp ein.

    1. Öffnen Sie unittest1.cpp.

    2. Fügen Sie diesen Code unter der #include "CppUnitTest.h" Zeile hinzu:

      #include "..\RooterLib\RooterLib.h"
      
  3. Fügen Sie einen Test hinzu, der die importierte Funktion verwendet. Fügen Sie den folgenden Code zu unittest1.cpp hinzu:

    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. Erstellen Sie die Projektmappe.

    Der neue Test wird im Test-Explorer im Knoten Nicht ausgeführte Tests angezeigt.

  5. Wählen Sie im Test-Explorer die Option Alle ausführen aus.

    Einfacher Test bestanden

Sie haben den Test und die Codeprojekte eingerichtet und überprüft, dass Sie Tests ausführen können, die Funktionen im Codeprojekt ausführen. Sie können jetzt damit beginnen, echte Tests und Code zu schreiben.

Steigern Sie die Tests iterativ, und schließen Sie sie erfolgreich ab.

  1. Fügen Sie einen neuen Test hinzu:

    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);
        }
    };
    

    Tipp

    Es wird empfohlen, dass Sie keine Tests ändern, die erfolgreich abgeschlossen wurden. Fügen Sie stattdessen einen neuen Test hinzu, aktualisieren Sie den Code, sodass der Test erfolgreich ist, und fügen Sie dann einen anderen Test hinzu. Fahren Sie auf diese Weise fort.

    Wenn die Benutzer ihre Anforderungen ändern, deaktivieren Sie die Tests, die nicht mehr richtig sind. Schreiben Sie neue Tests, und lassen Sie sie auf die gleiche stufenweise Art arbeiten.

  2. Wählen Sie im Test-Explorer die Option Alle ausführen aus.

  3. Der Test schlägt fehl.

    Fehler beim RangeTest

    Tipp

    Überprüfen Sie unmittelbar nach dem Schreiben eines Tests, dass er fehlschlägt. Damit vermeiden Sie den einfachen Fehler, einen Test zu schreiben, der nie fehlschlägt.

  4. Erweitern Sie den Code unter dem Test, damit der neue Test erfolgreich ist. Fügen Sie Folgendes zu RooterLib.cpp hinzu:

    #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. Erstellen Sie die Projektmappe, und wählen Sie dann im Test-Explorer Alle ausführen aus.

    Beide Tests sind erfolgreich.

Tipp

Entwickeln Sie Code, indem Sie einzeln Tests hinzufügen. Überprüfen Sie, dass nach jeder Iteration alle Tests erfolgreich sind.

Debuggen Sie einen fehlgeschlagenen Test.

  1. Fügen Sie einen anderen Test zu unittest1.cpp hinzu:

    // 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. Wählen Sie im Test-Explorer die Option Alle ausführen aus.

    Der Test schlägt fehl. Wählen Sie den Testnamen im Test-Explorer aus. Der Assertionsfehler wird hervorgehoben. Die Fehlermeldung wird im Detailbereich des Test-Explorers angezeigt.

    Fehler bei NegativeRangeTests

  3. Gehen Sie die Funktion durch, um herauszufinden, weshalb der Test fehlschlägt:

    1. Legen Sie einen Haltepunkt am Anfang der SquareRoot-Funktion fest.

    2. Wählen Sie im Kontextmenü des fehlgeschlagenen Tests die Option Ausgewählte Tests debuggen aus.

      Wenn die Ausführung am Haltepunkt beendet wird, gehen Sie den Code schrittweise durch.

    3. Fügen Sie Code zu RooterLib.cpp hinzu, um die Ausnahme zu erfassen:

      #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. Wählen Sie im Test-Explorer Alle ausführen aus, um die korrigierte Methode zu testen und zu überprüfen, dass Sie keine Regression eingeführt haben.

Alle Tests sind jetzt erfolgreich.

Alle Tests erfolgreich

Gestalten Sie den Code um, ohne die Tests zu ändern.

  1. Vereinfachen Sie die zentrale Berechnung in der SquareRoot-Funktion:

    // old code
    //result = result - (result*result - v)/(2*result);
    // new code
    result = (result + v/result) / 2.0;
    
  2. Wählen Sie Alle ausführen aus, um die umgestaltete Methode zu testen und zu überprüfen, dass Sie keine Regression eingeführt haben.

    Tipp

    Mit einem stabilen Satz guter Komponententests haben Sie mehr Gewissheit, dass Sie beim Ändern des Codes keine Fehler eingeführt haben.

    Umgestaltungen sollten von anderen Änderungen getrennt werden.