Unit test di codice nativo con Esplora test

In Visual Studio, è possibile creare test d'unità per il codice non gestito scritto in C++.Il codice non gestito talvolta denominato codice nativo.

La procedura riportata di seguito contiene le informazioni essenziali utili per iniziare.Nelle sezioni successive sarà fornita una procedura dettagliata in cui verranno descritti più dettagliatamente i passaggi.

Per scrivere un test d'unità per una DLL di codice non gestito

  1. Utilizzare il modello Native Test Project per creare un progetto di Visual Studio distinto per i test.

    Il progetto contiene un codice di test di esempio.

  2. Rendere la DLL accessibile al progetto di test:

    • #include un file .h che contiene le dichiarazioni delle funzioni esternamente accessibili della DLL.

      Il file .h deve contenere dichiarazioni di funzione contrassegnate con _declspec(dllimport).In alternativa, è possibile esportare i metodi utilizzando un file def.Per ulteriori informazioni, vedere Importazione ed esportazione.

      I test d'unità possono accedere solo alle funzioni esportate dalla DLL sottoposta a test.

    • Aggiungere il progetto DLL ai riferimenti del progetto di test:

      In Proprietà del progetto di test, espandere Proprietà comuni, Framework e riferimentie scegliere Aggiungi riferimento.

  3. Nel progetto di test, creare le classi e i metodi di test di test utilizzando le macro di TEST e asserzione la classe nel modo seguente:

    #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 contiene diverse funzioni statiche che è possibile utilizzare per verificare i risultati di un test.

    • Il parametro LINE_INFO() è facoltativo.Nei casi in cui non esiste alcun file PDB, consente al test runner di identificare la posizione di un errore.

    • È anche possibile scrivere metodi di impostazione e pulitura dei test.Per ulteriori informazioni, aprire la definizione della macro TEST_METHOD e leggere i commenti in CppUnitTest.h

    • Non è possibile annidare le classi di test.

  4. Utilizzo di Esplora test per eseguire i test:

    1. Nel menu Visualizza, scegliere Altre finestre, Esplora test.

    2. Compilare la soluzione di Visual Studio.

    3. In Esplora test, scegliere Esegui tutto.

    4. Per analizzare qualsiasi test più dettagliatamente con Esplora test:

      1. Selezionare il nome del test per visualizzare più dettagli, come un messaggio di errore ed una traccia dello stack.

      2. Aprire il nome del test (ad esempio facendo doppio clic) per passare alla posizione dell'errore o del codice di test.

      3. Dal menu di scelta rapida di un test, scegliere Debug del test selezionato per eseguire il test nel debugger.

Procedura dettagliata: Compilare una DLL non gestita con Esplora test

È possibile adattare questa procedura dettagliata per compilare il proprio DLL.La procedura principale è la seguente.

  1. Creare un progetto di test nativo.I test vengono creati in un progetto separato dalla DLL che si sta sviluppando.

  2. Creazione di un progetto DLL..In questa procedura dettagliata viene creato un nuovo file DLL, ma la procedura per testare una DLL esistente è simile.

  3. Definire le funzioni DLL visibili ai test.

  4. Aumentare i test iterativamente.Si consiglia un ciclo "red-green-refactor", in cui lo sviluppo di codice è condotto dai test.

  5. Debug di test falliti.È possibile eseguire test in modalità debug.

  6. Effettuare il refactoring mantenendo i test invariati.Il refactoring indica il miglioramento della struttura del codice senza modificarne il comportamento esterno.È possibile eseguirla per migliorare prestazioni, estensibilità, o la leggibilità del codice.Poiché lo scopo non è di modificare il comportamento, non si modificano i test mentre si esegue il refactoring del codice.I test consentono di verificare che non vengano inseriti bug durante il refactoring.È possibile apportare modifiche più facilmente che non senza i test.

  7. Controllo del code coverage.I test d'unità sono particolarmente utili quando eseguono più parti del codice.È possibile individuare quali parti del codice sono state utilizzate dai test.

  8. Unità isolate da risorse esterne.In genere, una DLL dipende da altri componenti del sistema che si sta sviluppando, come altre DLL, i database, o dei sottosistemi remoti.È utile verificare ogni unità in isolamento dalle relative dipendenze.I componenti esterni possono provocare un rallentamento dell'esecuzione dei test.Durante lo sviluppo, gli altri componenti potrebbero non essere completati.

Creare un progetto nativo di un test d'unità.

  1. Dal menu File, scegliere Nuovo, Progetto.

    Nella finestra di dialogo espandere, Installato, Modelli, Visual C++, Test.

    Scegliere il modello Progetto di test nativo.

    In questa procedura dettagliata, il progetto di test viene denominato NativeRooterTest.

    Creazione di un progetto unit test C++

  2. Il nuovo progetto viene aperto in unittest1.cpp.

    Progetto di test con TEST_CLASS e TEST_METHOD

    Notare che:

    • Ogni test è definito tramite TEST_METHOD(YourTestName){...}.

      Non è necessario scrivere una firma della funzione formale.La firma viene creata dalla macro TEST_METHOD.La macro genera un'istanza ad una funzione che restituisce un valore nullo.Viene inoltre generata una funzione statica che restituisce informazioni sul metodo di test.Queste informazioni consentono ad Esplora test di individuare il metodo.

    • I metodi dei test vengono raggruppati in classi utilizzando TEST_CLASS(YourClassName){...}.

      Quando vengono eseguiti i test, viene creata un'istanza di ogni classe dei test.I metodi di test vengono chiamati in un ordine non specificato.È possibile definire metodi speciali che vengono richiamati prima e dopo ogni modulo, classe, o metodo.Per ulteriori informazioni, vedere Organizing C++ Tests.

  3. Verificare che i test vengano eseguiti in Esplora test:

    1. Inserire il codice di test:

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

      Si noti che la classe Assert fornisce diversi metodi statici che è possibile utilizzare per verificare i risultati nei metodi di test.

    2. Dal menu Test, scegliere Esegui , Tutti i test.

      Compilazione ed esecuzione dei test.

      Verrà visualizzato Esplora test.

      Il test verrà visualizzato in Test superati.

      Esplora unit test con un test superato

Creare un progetto DLL non gestito.

  1. Creare un progetto Visual C++ tramite il modello Progetto Win32.

    In questa procedura dettagliata, il progetto viene denominato RootFinder.

    Creazione di un progetto Win32 C++

  2. Selezionare DLL ed Esporta simboli nella creazione guidata applicazione Win32.

    L'opzione Esporta simboli genera una semplice macro che è possibile utilizzare per dichiarare i metodi esportati.

    Creazione progetto C++ impostata per simboli di esportazione e DLL

  3. Dichiarare una funzione esportata nel file .h principale:

    Nuovo progetto di codice DLL e file h con macro API

    Il dichiaratore __declspec(dllexport) permette ai membri public e protected della classe di essere visibili al di fuori della DLL.Per ulteriori informazioni, vedere Tramite l'attributo dllimport e di dllexport nelle classi C++.

  4. Nel file .cpp principale, aggiungere il corpo minimo della funzione:

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

Accoppiare il progetto del test al progetto DLL

  1. Aggiungere il progetto DLL ai riferimenti del progetto di test:

    1. Aprire le proprietà del progetto di test e scegliere Proprietà comuni, Framework e riferimenti.

      Proprietà progetto C++ - Framework e riferimenti

    2. Scegliere Aggiungi nuovo riferimento.

      Nella finestra di dialogo Aggiungi riferimento, selezionare il progetto DLL e scegliere Aggiungi.

      Proprietà progetto C++ - Aggiungi nuovo riferimento

  2. Nel file principale .cpp del test d'unità, includere il file .h del codice DLL:

    #include "..\RootFinder\RootFinder.h"
    
  3. Aggiungere un test di base che utilizza la funzione esportata:

    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. Compilare la soluzione.

    Il nuovo test viene visualizzato in Esplora test.

  5. In Esplora test, scegliere Esegui tutto.

    Esplora unit test - Test di base superato

È stato installato il test e i progetti di codice, e verificato che sia possibile eseguire test che eseguono funzioni nel progetto di codice.Ora è possibile iniziare a scrivere test e codici reali.

In maniera iterativa aumentare i test e farli superare

  1. Aggiungere un nuovo 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);
      }
    }
    
    SuggerimentoSuggerimento

    È consigliabile non modificare i test che siano stati superati.Al contrario, aggiungere un nuovo test, aggiornare il codice in modo che il test passi e quindi aggiungere un altro test, e così via.

    Quando gli utenti modificano i requisiti, disabilitare i test che non sono più corretti.Scrivere nuovi test e farli funzionare uno alla volta, nello stesso modo incrementale.

  2. Compilare la soluzione e quindi in Esplora test, scegliere Esegui tutto.

    Il nuovo test fallisce.

    RangeTest non riuscito

    SuggerimentoSuggerimento

    Verificare che ogni test fallisca subito dopo averlo scritto.Questo consente di evitare il semplice errore di scrivere un test che non fallisce mai.

  3. Aumentare il codice sotto test in modo che il nuovo test passi:

    #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. Compilare la soluzione e quindi in Esplora test, scegliere Esegui tutto.

    Entrambi i test vengono superati.

    Esplora unit test - Test intervallo superato

    SuggerimentoSuggerimento

    Sviluppare il codice aggiungendo un test alla volta.Assicurarsi che tutti i test vengano superati dopo ogni iterazione.

Debug di un test fallito.

  1. Aggiungere un altro 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. Compilare la soluzione e scegliere Esegui tutto.

  3. Aprire (o fare doppio clic) sul test non superato.

    L'asserzione fallita viene evidenziata.Il messaggio di errore è visibile nel riquadro dei dettagli di Esplora test.

    NegativeRangeTests non riuscito

  4. Per capire perché il test fallisca, scorrere la funzione:

    1. Impostare il punto di interruzione all'inizio della funzione SquareRoot.

    2. Dal menu di scelta rapida del test non superato, scegliere Esegui debug test selezionati.

      Quando l'esecuzione si ferma in corrispondenza del punto di interruzione, eseguire il codice un'istruzione alla volta.

  5. Inserire il codice nella funzione che si sta sviluppando:

    #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. Tutti i test vengono ora superati.

    Tutti i test superati

Effettuare il refactoring del codice senza modificare i test

  1. Semplificare il calcolo centrale nella funzione SquareRoot:

    // old code:
    //   result = result - (result*result - v)/(2*result);
    // new code:
         result = (result + v/result)/2.0;
    
  2. Compilare la soluzione e scegliere Esegui tutto, per assicurarsi che non sia stato introdotto un errore.

    SuggerimentoSuggerimento

    Un buon set di test d'unità fornisce la fiducia che non siano stati introdotti bug con la modifica del codice.

    Mantenere il refactoring separato da altre modifiche.

Passaggi successivi

  • Isolamento. La maggior parte delle DLL dipendono da altri sottosistemi quali database ed altre DLL.Queste altre componenti vengono spesso sviluppate in parallelo.Per permettere l'esecuzione di test d'unità mentre gli altri componenti non sono ancora disponibili è necessario sostituirli con una simulazione.

  • Test di verifica della compilazione (BVT). È possibile eseguire test sul server di compilazione del team ad intervalli predefiniti.Questo assicura che i bug non verranno introdotti quando il lavoro dei diversi membri del team viene integrato.

  • Test di controllo. È possibile lasciare che alcuni test vengano eseguiti prima che ogni membro del team esegua controllo del codice sorgente.In genere questo è un sottoinsieme del set completo dei test di verifica della compilazione.

    È inoltre possibile lasciare al chiamante un livello minimo di code coverage.

Vedere anche

Attività

Walkthrough: Creating and Using a Dynamic Link Library (C++)

Concetti

Importazione ed esportazione

Altre risorse

Cenni preliminari di codice interoperabile gestito/non gestito

Debug del codice nativo