Exercice : Effectuer un test de couverture du code

Effectué

À l’image des outils que vous utilisez pour les tests unitaires, l’outil dont vous vous servez pour la couverture du code dépend du langage de programmation et du framework d’application.

Quand vous ciblez des applications .NET à exécuter sur Linux, coverlet est une option classique. Coverlet est une bibliothèque de couverture du code multiplateforme pour .NET.

Comment la couverture du code est-elle effectuée dans .NET ?

La façon dont vous collectez la couverture du code dépend du langage de programmation et des frameworks que vous utilisez, ainsi que des outils de couverture du code disponibles.

Dans notre scénario Tailspin, nous constatons que :

  • Visual Studio sur Windows offre un moyen d’effectuer la couverture du code.

  • Toutefois, comme nous effectuons la génération sur Linux, nous pouvons utiliser coverlet, une bibliothèque de couverture du code multiplateforme pour .NET.

    Le projet de test unitaire nécessite le package NuGet coverlet.msbuild.

  • Les résultats de la couverture du code sont écrits dans un fichier XML afin qu’ils puissent être traités par un autre outil. Azure Pipelines prend en charge les formats de résultats de couverture Cobertura et JaCoCo.

    Pour ce module, nous utilisons Cobertura.

  • Pour convertir les résultats de couverture Cobertura dans un format lisible par l’homme, nous pouvons utiliser un outil appelé ReportGenerator.

  • ReportGenerator fournit plusieurs formats, comme HTML. Les formats HTML créent des rapports détaillés pour chaque classe dans un projet .NET.

    Plus précisément, il existe un format HTML appelé HtmlInline_AzurePipelines qui fournit une apparence visuelle correspondant à Azure Pipelines.

Comment gérer les outils .NET ?

Un outil .NET comme ReportGenerator est un package NuGet spécial qui contient une application console. Vous pouvez gérer un outil .NET comme un outil global ou un outil local.

Un outil global est installé à un emplacement centralisé et peut être appelé à partir de n’importe quel répertoire. Une seule version d’un outil global est utilisée pour tous les répertoires sur la machine.

Un outil local est une copie plus isolée d’un outil .NET qui est limitée à un répertoire spécifique. L’étendue permet à différents répertoires de contenir différentes versions du même outil.

Vous utilisez un fichier manifeste pour gérer les outils locaux pour un répertoire donné. Ce fichier est au format JSON et est généralement nommé dotnet-tools.json. Un fichier manifeste vous permet de décrire les versions d’outils spécifiques dont vous avez besoin pour générer ou exécuter votre application.

Quand vous incluez le fichier manifeste dans le contrôle de code source et vos sources d’application, les développeurs et les systèmes de génération peuvent exécuter la commande dotnet tool restore pour installer l’ensemble des outils répertoriés dans le fichier manifeste. Quand vous avez besoin d’une version plus récente d’un outil local, il vous suffit de mettre à jour la version dans le fichier manifeste.

Pour que les choses soient plus isolées, vous utilisez des outils locaux dans ce module. Vous allez créer un manifeste d’outils qui inclut l’outil ReportGenerator. Vous allez également modifier votre pipeline de build pour installer l’outil ReportGenerator afin de convertir les résultats de la couverture du code en un format lisible.

Exécuter une couverture du code localement

Avant d’écrire du code de pipeline, vous pouvez effectuer des essais manuellement pour vérifier le processus.

  1. Dans Visual Studio Code, ouvrez le terminal intégré.

  2. Exécutez la commande dotnet new suivante pour créer un fichier manifeste d’outils locaux.

    dotnet new tool-manifest
    

    La commande crée un fichier nommé .config/dotnet-tools.json.

  3. Exécutez la commande dotnet tool install suivante pour installer ReportGenerator :

    dotnet tool install dotnet-reportgenerator-globaltool
    

    Cette commande installe la version la plus récente de ReportGenerator et ajoute une entrée au fichier manifeste d’outils.

  4. Exécutez la commande dotnet add package suivante pour ajouter le package coverlet.msbuild au projet Tailspin.SpaceGame.Web.Tests :

    dotnet add Tailspin.SpaceGame.Web.Tests package coverlet.msbuild
    
  5. Exécutez la commande dotnet test suivante pour exécuter vos tests unitaires et collecter la couverture du code :

    Remarque

    Si vous utilisez le terminal PowerShell dans Visual Studio, le caractère de continuation de ligne est un backtick (`), donc utilisez ce caractère à la place du caractère de barre oblique inverse (\) pour les commandes à plusieurs lignes.

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

    Si la commande échoue, essayez de l’exécuter comme suit :

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

    Cette commande ressemble à celle que vous avez exécutée. Les indicateurs /p: indiquent à coverlet le format de couverture du code à utiliser et où placer les résultats.

  6. Exécutez la commande dotnet tool run suivante pour utiliser ReportGenerator afin de convertir le fichier Cobertura au format HTML :

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

    De nombreux fichiers HTML apparaissent dans le dossier CodeCoverage à la racine du projet.

  7. Dans Visual Studio Code, développez le dossier CodeCoverage, cliquez avec le bouton droit sur index.htm, puis sélectionnez Révéler dans l’Explorateur de fichiers (Révéler dans le Finder sur macOS ou Ouvrir le dossier contenant sur Linux).

  8. Dans l’Explorateur Windows (Finder sur macOS), double-cliquez sur index.htm pour l’ouvrir dans un navigateur web.

    Le récapitulatif du rapport de couverture apparaît.

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

  9. Faites défiler la page vers le bas pour voir une décomposition de la couverture par type de classe.

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

  10. Sélectionnez le lien TailSpin.SpaceGame.Web.LocalDocumentDBRepository<T> pour voir plus de détails.

    Notez que la méthode GetItemsAsync est couverte par les tests unitaires, mais que la méthode CountItemsAsync n’a pas de couverture.

    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).

    C’est normal, car la méthode de test FetchOnlyRequestedGameRegion appelle la méthode GetItemsAsync, mais pas la méthode CountItemsAsync. (Pour passer en revue le code de test, observez le fichier DocumentDBRepository_GetItemsAsyncShould.cs.)

Créer une branche

Maintenant que vous pouvez générer localement un rapport de couverture du code, vous êtes prêt à ajouter des tâches à votre pipeline de build, qui effectue les mêmes tâches.

Dans cette section, vous allez créer une branche nommée code-coverage, basée sur la branche unit-tests, destinée à conserver votre travail. Dans la pratique, vous devriez normalement créer cette branche à partir de la branche main.

  1. Dans Visual Studio Code, ouvrez le terminal intégré.

  2. Dans le terminal, exécutez la commande git checkout suivante pour créer une branche nommée code-coverage :

    git checkout -B code-coverage
    

Ajouter des tâches de génération

Dans cette section, vous allez ajouter des tâches qui mesurent la couverture du code à votre pipeline de build.

  1. Dans Visual Studio Code, modifiez azure-pipelines.yml comme suit :

    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()
    

    Cette version s’appuie sur votre configuration existante. Voici un récapitulatif des nouveautés :

    Tâche Azure Pipelines Nom complet Description
    DotNetCoreCLI@2 Installer des outils .NET à partir du manifeste local Installe les outils listés dans le fichier manifeste, dotnet-tools.json
    DotNetCoreCLI@2 Run unit tests - $(buildConfiguration) Exécute des tests unitaires et collecte également la couverture du code dans le format Cobertura
    DotNetCoreCLI@2 Create code coverage report Convertit la sortie Cobertura au format HTML
    PublishCodeCoverageResults@1 Publish code coverage report Publie le rapport dans le pipeline

Commiter vos modifications et pousser la branche vers GitHub

Ici, vous poussez vos modifications vers GitHub et vous voyez le pipeline s’exécuter. Rappelez-vous que vous êtes actuellement dans la branche code-coverage.

Bien que ce ne soit pas obligatoire, vous allez ajouter et valider ici chaque fichier séparément afin que chaque modification soit associée à un message de validation descriptif.

  1. Dans Visual Studio Code, accédez au terminal.

  2. Ajoutez et commitez le fichier Tailspin.SpaceGame.Web.Tests.csproj, qui contient maintenant une référence au package coverlet.msbuild :

    git add Tailspin.SpaceGame.Web.Tests/Tailspin.SpaceGame.Web.Tests.csproj
    git commit -m "Add coverlet.msbuild package"
    
  3. Ajoutez et commitez le fichier manifeste des outils, dotnet-tools.json :

    git add .config/dotnet-tools.json
    git commit -m "Add code coverage"
    
  4. Ajoutez et commitez azure-pipelines.yml, qui contient votre configuration de build mise à jour :

    git add azure-pipelines.yml
    git commit -m "Add code coverage"
    
  5. Poussez la branche code-coverage vers GitHub.

    git push origin code-coverage
    

Regarder Azure Pipelines exécuter les tests

Ici, vous allez voir les tests s’exécuter dans le pipeline, puis visualiser les résultats à partir d’Azure Test Plans.

  1. Dans Azure Pipelines, suivez la build à travers chacune des étapes.

  2. Quand la build se termine, revenez à la page Résumé et sélectionnez l’onglet Couverture du code.

    Vous voyez les mêmes résultats que ceux obtenus quand vous avez exécuté les tests localement.

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

    En guise d’étape facultative, vous pouvez explorer les résultats à partir d’Azure Pipelines.

Ajouter le widget de tableau de bord

Dans la section précédente, vous avez ajouté le widget Tendance des résultats des tests à votre tableau de bord, ce qui permet à d’autres utilisateurs d’examiner rapidement les tendances des résultats des tests au fil du temps.

Ici, vous allez ajouter un deuxième widget qui récapitule la couverture du code.

  1. Sous un nouvel onglet de navigateur, accédez à marketplace.visualstudio.com.

  2. Sous l’onglet Azure DevOps, recherchez couverture du code.

  3. Sélectionnez Widgets de couverture du code (publiés par Shane Davis).

  4. Sélectionnez Get it free (Obtenir gratuitement).

  5. Dans la zone de liste déroulante, sélectionnez votre organisation Azure DevOps.

  6. Sélectionnez Installer.

  7. Revenez à Azure DevOps.

  8. Accédez à Vue d’ensemble>Tableaux de bord.

  9. Sélectionnez Modifier.

  10. Recherchez Code Coverage, puis sélectionnez Code Coverage.

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

  11. Faites glisser Code Coverage vers le canevas.

  12. Sélectionnez l’icône d’engrenage pour configurer le widget.

  13. Gardez les valeurs par défaut pour tous les paramètres, à l’exception de :

    • Largeur : Entrez 2
    • Définition de build : Sélectionner votre pipeline
    • Mesure de couverture : sélectionner des Lignes
  14. Cliquez sur Enregistrer.

  15. Sélectionnez Modification terminée.

    Le widget affiche le pourcentage de code couvert par vos tests unitaires.

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

La couverture du code est maintenant configurée dans votre pipeline. Bien que votre couverture du code existante soit faible, vous disposez d’une base de référence que vous pouvez améliorer au fil du temps.

Ensuite, vous pouvez configurer coverlet pour vérifier si vos tests fournissent un seuil de couverture minimal. Votre seuil peut être 30, 50 ou 80 % de couverture, en fonction de vos besoins. La build échoue si vos tests couvrent une quantité inférieure au seuil.

Supprimer les fichiers de couverture du code

Souvenez-vous : quand vous avez exécuté Reportgenerator, vous avez vu apparaître une série de fichiers HTML dans le dossier CodeCoverage à la racine du projet.

Ces fichiers HTML ne sont pas destinés à être inclus dans le contrôle de code source. Vous n’en avez plus besoin. Bien que le fichier .gitignore du projet soit déjà configuré pour ignorer ce qui se trouve dans le répertoire CodeCoverage, il est judicieux de supprimer ces fichiers pour qu’ils ne soient pas ajoutés à votre dépôt Git dans les modules ultérieurs.

Dans Visual Studio Code, accédez à la fenêtre de terminal, puis dans le répertoire racine de votre projet, exécutez cette commande :

rm -rf CodeCoverage/