Esercizio - Eseguire i test di code coverage

Completato

In modo simile allo strumento usato per il testing unità, lo strumento usato per il code coverage dipende dal linguaggio di programmazione e dal framework applicazione.

Se le applicazioni .NET sono destinate all'esecuzione in Linux, una delle opzioni più diffuse è coverlet. Coverlet è una libreria di code coverage multipiattaforma per .NET.

Come viene eseguito il code coverage in .NET?

Il modo in cui si raccolgono i dati di code coverage dipende dal linguaggio di programmazione e dal framework in uso, oltre che dagli strumenti disponibili.

Nello scenario di Tailspin è stato rilevato che:

  • Visual Studio in Windows offre un'opzione per eseguire il code coverage.

  • Poiché si sta creando in Linux, è tuttavia possibile usare coverlet, una libreria di code coverage multipiattaforma per .NET.

    Il progetto di unit test richiede il pacchetto NuGet coverlet.msbuild.

  • I risultati del code coverage vengono scritti in un file XML, in modo che possano essere elaborati da un altro strumento. Azure Pipelines supporta i formati dei risultati Cobertura e JaCoCo.

    Per questo modulo si usa Cobertura.

  • Per convertire i risultati Cobertura in un formato leggibile, è possibile usare uno strumento chiamato ReportGenerator.

  • ReportGenerator offre molti formati, tra cui HTML. I formati HTML creano report dettagliati per ogni classe in un progetto .NET.

    In particolare, il formato HTML denominato HtmlInline_AzurePipelines fornisce una rappresentazione visiva che corrisponde ad Azure Pipelines.

Come è possibile gestire gli strumenti .NET?

Uno strumento .NET, come ad esempio ReportGenerator, è uno speciale pacchetto NuGet che contiene un'applicazione console. È possibile gestire uno strumento .NET come strumento globale o locale.

Uno strumento globale viene installato in una posizione centralizzata e può essere chiamato da qualsiasi directory. Per tutte le directory nel computer viene usata una versione di uno strumento globale.

Uno strumento locale è una copia più isolata di uno strumento .NET il cui ambito è una directory specifica. L'ambito consente a directory diverse di contenere versioni diverse dello stesso strumento.

Per gestire gli strumenti locali per una determinata directory viene usato un file manifesto. Il file è in formato JSON ed è in genere denominato dotnet-tools.json. Un file manifesto consente di descrivere le versioni specifiche dello strumento necessarie per compilare o eseguire l'applicazione.

Quando si include il file manifesto nel controllo del codice sorgente insieme alle origini dell'applicazione, gli sviluppatori e i sistemi di compilazione possono eseguire il comando dotnet tool restore per installare tutti gli strumenti elencati nel file manifesto. Quando è necessaria una versione più recente di uno strumento locale, è sufficiente aggiornare la versione nel file manifesto.

Per isolare maggiormente le operazioni, in questo modulo si useranno gli strumenti locali. Si creerà il manifesto di uno strumento, incluso lo strumento ReportGenerator. Si modificherà inoltre la pipeline di compilazione per installare lo strumento ReportGenerator in modo da convertire i risultati del code coverage in un formato chiaramente leggibile.

Eseguire il code coverage in locale

Prima di scrivere codice della pipeline, è possibile provare manualmente le operazioni per verificare il processo.

  1. Aprire il terminale integrato in Visual Studio Code.

  2. Eseguire il comando dotnet new seguente per creare un file manifesto dello strumento locale.

    dotnet new tool-manifest
    

    Il comando crea un file denominato .config/dotnet-tools.json.

  3. Eseguire il comando dotnet tool install seguente per installare ReportGenerator:

    dotnet tool install dotnet-reportgenerator-globaltool
    

    Con questo comando viene installata la versione più recente di ReportGenerator e viene aggiunta una voce al file manifesto dello strumento.

  4. Eseguire il comando dotnet add package seguente per aggiungere il pacchetto coverlet.msbuild al progetto Tailspin.SpaceGame.Web.Tests:

    dotnet add Tailspin.SpaceGame.Web.Tests package coverlet.msbuild
    
  5. Eseguire il comando dotnet test seguente per eseguire gli unit test e raccogliere i dati di code coverage:

    Nota

    Se si usa il terminale di PowerShell in Visual Studio, il carattere di continuazione della riga è un backtick (`), quindi usare tale carattere al posto del carattere barra rovesciata (\) per i comandi da più righe.

    dotnet test --no-build \
      --configuration Release \
      /p:CollectCoverage=true \
      /p:CoverletOutputFormat=cobertura \
      /p:CoverletOutput=./TestResults/Coverage/
    

    Se il comando non riesce, provare a eseguirlo come illustrato di seguito:

    MSYS2_ARG_CONV_EXCL="*" dotnet test --no-build \
      --configuration Release \
      /p:CollectCoverage=true \
      /p:CoverletOutputFormat=cobertura \
      /p:CoverletOutput=./TestResults/Coverage/
    

    Questo comando è simile a quello eseguito in precedenza. I flag /p: indicano a Coverlet quale formato di code coverage usare e dove inserire i risultati.

  6. Eseguire il comando dotnet tool run seguente per usare ReportGenerator in modo da convertire il file di Cobertura in HTML:

    dotnet tool run reportgenerator \
      -reports:./Tailspin.SpaceGame.Web.Tests/TestResults/Coverage/coverage.cobertura.xml \
      -targetdir:./CodeCoverage \
      -reporttypes:HtmlInline_AzurePipelines
    

    Nella cartella CodeCoverage alla radice del progetto sono presenti numerosi file HTML.

  7. In Visual Studio Code espandere la cartella CodeCoverage, fare clic con il pulsante destro del mouse su index.htm, quindi selezionare Visualizza in Esplora risorse (Mostra nel Finder in macOS o Apri cartella contenitore in Linux).

  8. In Esplora risorse (Finder in macOS) fare doppio clic sul file index.htm per aprirlo in un Web browser.

    Verrà visualizzato il report riepilogativo di code coverage.

    A screenshot of the local code coverage report summary showing 7.7 percent line coverage.

  9. Scorrere alla fine della pagina per visualizzare una suddivisione del code coverage per tipo di classe.

    A screenshot of local coverage report class summary showing coverage stats across classes found in the Tailspin.SpaceGame.Web code.

  10. Per visualizzare altri dettagli, selezionare il collegamento a TailSpin.SpaceGame.Web.LocalDocumentDBRepository<T>.

    Si noti che il metodo GetItemsAsync è coperto dagli unit test, mentre il metodo CountItemsAsync non ha copertura.

    A screenshot of local class coverage detail with a visual representation of unit test coverage for two C# methods, one with all code lines green (covered) and one with all lines red (not covered).

    Questo risultato è previsto, perché il metodo di test FetchOnlyRequestedGameRegion chiama il metodo GetItemsAsync, ma non il metodo CountItemsAsync. Per esaminare il codice di test, vedere il file DocumentDBRepository_GetItemsAsyncShould.cs.

Creare un ramo

Ora che si è in grado di compilare un report di code coverage in locale, è possibile aggiungere attività alla pipeline di compilazione, che esegue le stesse attività.

In questa sezione verrà creato un ramo denominato code-coverage, basato sul ramo unit-tests, per contenere il lavoro. In pratica, questo ramo viene solitamente creato dal ramo main.

  1. Aprire il terminale integrato in Visual Studio Code.

  2. Nel terminale eseguire il comando git checkout seguente per creare un ramo denominato code-coverage:

    git checkout -B code-coverage
    

Aggiungere attività di compilazione

In questa sezione verranno aggiunte alla pipeline di compilazione le attività che misurano il code coverage.

  1. In Visual Studio Code modificare azure-pipelines.yml come indicato di seguito:

    trigger:
    - '*'
    
    pool:
      vmImage: 'ubuntu-20.04'
      demands:
      - npm
    
    variables:
      buildConfiguration: 'Release'
      wwwrootDir: 'Tailspin.SpaceGame.Web/wwwroot'
      dotnetSdkVersion: '6.x'
    
    steps:
    - task: UseDotNet@2
      displayName: 'Use .NET SDK $(dotnetSdkVersion)'
      inputs:
        version: '$(dotnetSdkVersion)'
    
    - task: Npm@1
      displayName: 'Run npm install'
      inputs:
        verbose: false
    
    - script: './node_modules/.bin/node-sass $(wwwrootDir) --output $(wwwrootDir)'
      displayName: 'Compile Sass assets'
    
    - task: gulp@1
      displayName: 'Run gulp tasks'
    
    - script: 'echo "$(Build.DefinitionName), $(Build.BuildId), $(Build.BuildNumber)" > buildinfo.txt'
      displayName: 'Write build info'
      workingDirectory: $(wwwrootDir)
    
    - task: DotNetCoreCLI@2
      displayName: 'Restore project dependencies'
      inputs:
        command: 'restore'
        projects: '**/*.csproj'
    
    - task: DotNetCoreCLI@2
      displayName: 'Build the project - $(buildConfiguration)'
      inputs:
        command: 'build'
        arguments: '--no-restore --configuration $(buildConfiguration)'
        projects: '**/*.csproj'
    
    - task: DotNetCoreCLI@2
      displayName: 'Install .NET tools from local manifest'
      inputs:
        command: custom
        custom: tool
        arguments: 'restore'
    
    - task: DotNetCoreCLI@2
      displayName: 'Run unit tests - $(buildConfiguration)'
      inputs:
        command: 'test'
        arguments: '--no-build --configuration $(buildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(Build.SourcesDirectory)/TestResults/Coverage/'
        publishTestResults: true
        projects: '**/*.Tests.csproj'
    
    - task: DotNetCoreCLI@2
      displayName: 'Create code coverage report'
      inputs:
        command: custom
        custom: tool
        arguments: 'run reportgenerator -reports:$(Build.SourcesDirectory)/**/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/CodeCoverage -reporttypes:HtmlInline_AzurePipelines'
    
    - task: PublishCodeCoverageResults@1
      displayName: 'Publish code coverage report'
      inputs:
        codeCoverageTool: 'cobertura'
        summaryFileLocation: '$(Build.SourcesDirectory)/**/coverage.cobertura.xml'
    
    - task: DotNetCoreCLI@2
      displayName: 'Publish the project - $(buildConfiguration)'
      inputs:
        command: 'publish'
        projects: '**/*.csproj'
        publishWebProjects: false
        arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/$(buildConfiguration)'
        zipAfterPublish: true
    
    - task: PublishBuildArtifacts@1
      displayName: 'Publish Artifact: drop'
      condition: succeeded()
    

    Questa versione si basa sulla configurazione esistente. Ecco un riepilogo delle novità:

    Attività di Azure Pipelines Nome visualizzato Descrizione
    DotNetCoreCLI@2 Installa strumenti .NET Core da un manifesto locale Installa gli strumenti elencati nel file manifesto, dotnet-tools.json
    DotNetCoreCLI@2 Esegui unit test - $(buildConfiguration) Esegue gli unit test e raccoglie anche i dati di code coverage in formato Cobertura
    DotNetCoreCLI@2 Crea il report di code coverage Converte l'output di Cobertura in HTML
    PublishCodeCoverageResults@1 Pubblica il report di code coverage Pubblica il report nella pipeline

Eseguire il commit delle modifiche e il push del ramo in GitHub

A questo punto si eseguirà il push delle modifiche in GitHub e si assisterà all'esecuzione della pipeline. Tenere presente che attualmente ci si trova nel ramo code-coverage.

Sebbene non sia obbligatorio, ora si aggiungerà ogni file separatamente e si eseguirà il commit in modo che ciascuna modifica venga associata a un messaggio di commit descrittivo.

  1. In Visual Studio Code passare al terminale.

  2. Aggiungere il file Tailspin.SpaceGame.Web.Tests.csproj, che ora contiene un riferimento al pacchetto coverlet.msbuild, ed eseguire il commit:

    git add Tailspin.SpaceGame.Web.Tests/Tailspin.SpaceGame.Web.Tests.csproj
    git commit -m "Add coverlet.msbuild package"
    
  3. Aggiungere il file manifesto dello strumento, dotnet-tools.json, ed eseguire il commit:

    git add .config/dotnet-tools.json
    git commit -m "Add code coverage"
    
  4. Aggiungere azure-pipelines.yml, che contiene la configurazione della build aggiornata, ed eseguire il commit:

    git add azure-pipelines.yml
    git commit -m "Add code coverage"
    
  5. Eseguire il push del ramo code-coverage in GitHub.

    git push origin code-coverage
    

Osservare l'esecuzione dei test in Azure Pipelines

Ora verranno visualizzati test eseguiti nella pipeline e quindi i risultati prodotti da Azure Test Plans.

  1. In Azure Pipelines tenere traccia della compilazione in ognuno dei passaggi.

  2. Al completamento della build, tornare alla pagina di riepilogo e selezionare la scheda Code coverage.

    Vengono visualizzati gli stessi risultati generati quando i test sono stati eseguiti in locale.

    A screenshot of Azure Pipelines showing the Code Coverage tab, with code coverage report summary showing 7.7 percent line coverage.

    Come passaggio facoltativo, è possibile esplorare i risultati in Azure Pipelines.

Aggiungere il widget del dashboard

Nella sezione precedente è stato aggiunto al dashboard il widget Tendenza risultati del test, che consente agli altri utenti di esaminare rapidamente le tendenze dei risultati del test nel corso del tempo.

A questo punto verrà aggiunto un secondo widget per riepilogare i dati di code coverage.

  1. In una nuova scheda del browser passare a marketplace.visualstudio.com.

  2. Nella scheda Azure DevOps cercare code coverage.

  3. Selezionare "Code Coverage Widgets" (pubblicato da Shane Davis).

  4. Selezionare Scarica gratuitamente.

  5. Nell'elenco a discesa selezionare l'organizzazione di Azure DevOps.

  6. Selezionare Installa.

  7. Tornare ad Azure DevOps.

  8. Passare a Panoramica>Dashboard.

  9. Seleziona Modifica

  10. Cercare code coverage, quindi selezionare Code coverage.

    A screenshot of Visual Studio Marketplace showing the Code Coverage widget card.

  11. Trascinare Code coverage nel canvas.

  12. Per configurare il widget, selezionare l'icona dell'ingranaggio.

  13. Mantenere tutte le impostazioni predefinite, ad eccezione di:

    • Larghezza: Immettere 2
    • Definizione di compilazione: Selezionare la pipeline
    • Misurazione della copertura: selezionare Linee
  14. Seleziona Salva.

  15. Selezionare Modifica completata.

    Il widget mostra la percentuale di code coverage degli unit test.

    A screenshot of Azure DevOps Code Coverage widget showing 8 percent coverage of the sample project.

A questo punto il code coverage è configurato nella pipeline. Anche se l'attuale percentuale di code coverage è bassa, è disponibile una baseline che è possibile migliorare nel corso del tempo.

In seguito è possibile configurare Coverlet in modo che verifichi se i test forniscono una soglia minima di copertura. La soglia potrebbe essere pari al 30%, al 50% o all'80% di copertura, a seconda dei propri requisiti. La compilazione avrà esito negativo se i test coprono un valore inferiore a questo importo.

Rimuovere i file di code coverage

Ricordare che, quando è stato eseguito Reportgenerator in precedenza, nella cartella CodeCoverage alla radice del progetto sono comparsi numerosi file HTML.

Questi file HTML non sono destinati a essere inclusi nel controllo del codice sorgente e non sono più necessari. Anche se il file con estensione gitignore del progetto è già configurato in modo da ignorare qualsiasi contenuto della directory CodeCoverage, è opportuno eliminare questi file affinché non vengano aggiunti al repository Git nei moduli futuri.

In Visual Studio Code passare alla finestra del terminale, quindi, nella directory radice del progetto eseguire il comando seguente:

rm -rf CodeCoverage/