Exercice : Corriger un test ayant échoué

Effectué

À ce stade, vous disposez d’un moyen d’exécuter des tests unitaires quand les modifications progressent dans le pipeline de build. Vous avez également un moyen de mesurer la quantité de code couverte par vos tests.

Il est toujours judicieux d’exécuter vos tests localement avant d’envoyer les modifications au pipeline. Mais que se passe-t-il quand quelqu’un oublie et envoie une modification qui endommage la build ?

Dans cette unité, vous corrigerez un build défectueux causé par un test unitaire défaillant. Vous pourrez ainsi :

  • Obtenir le code de démarrage auprès de GitHub.
  • Ajouter des outils de couverture du code à votre projet.
  • Pousser (push) le code vers le dépôt.
  • Regarder le pipeline s’exécuter automatiquement et les tests unitaires échouer.
  • Reproduire l’échec localement.
  • Analyser et corriger l’échec.
  • Pousser (push) un correctif et regarder la build réussir.

Passer en revue le nouveau test unitaire

La dernière caractéristique de l'équipe concerne le leaderboard. Nous devons obtenir le nombre de scores de la base de données, afin d'écrire un test unitaire pour vérifier la méthode IDocumentDBRepository<T>.GetItemsAsync.

Voici à quoi ressemble le test. Vous n’avez pas besoin d’ajouter de code pour l’instant.

[TestCase(0, ExpectedResult=0)]
[TestCase(1, ExpectedResult=1)]
[TestCase(10, ExpectedResult=10)]
public int ReturnRequestedCount(int count)
{
    const int PAGE = 0; // take the first page of results

    // Fetch the scores.
    Task<IEnumerable<Score>> scoresTask = _scoreRepository.GetItemsAsync(
        score => true, // return all scores
        score => 1, // we don't care about the order
        PAGE,
        count // fetch this number of results
    );
    IEnumerable<Score> scores = scoresTask.Result;

    // Verify that we received the specified number of items.
    return scores.Count();
}

Rappelez-vous que, dans un test NUnit, TestCase fournit des données inline à utiliser pour tester cette méthode. NUnit appelle la méthode de test unitaire ReturnRequestedCount comme suit :

ReturnRequestedCount(0);
ReturnRequestedCount(1);
ReturnRequestedCount(10);

Ce test utilise également la propriété ExpectedResult pour simplifier le code de test et clarifier sa finalité. NUnit compare automatiquement la valeur de retour à la valeur de cette propriété ; ainsi, il n’est pas nécessaire d’appeler l’assertion explicitement.

Nous allons choisir quelques valeurs qui représentent des requêtes typiques. Nous allons également inclure 0 pour couvrir ce cas limite.

Récupérer (fetch) la branche à partir de GitHub

Comme dans le cas précédent, récupérez la branche failed-test sur GitHub et extrayez-la (ou basculez vers elle).

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

  2. Exécutez les commandes git fetch et git checkout suivantes pour télécharger une branche nommée failed-test à partir du dépôt Microsoft, puis basculez sur cette branche :

    git fetch upstream failed-test
    git checkout -B failed-test upstream/failed-test
    

    Pour cette formation, nous allons appeler la branche failed-test. En pratique, vous nommerez une branche d'après son objectif ou sa caractéristique.

  3. Exécutez les commandes suivantes pour créer un fichier manifeste d’outils locaux, installez l’outil ReportGenerator, puis ajoutez le package coverlet.msbuild à votre projet de tests :

    dotnet new tool-manifest
    dotnet tool install dotnet-reportgenerator-globaltool
    dotnet add Tailspin.SpaceGame.Web.Tests package coverlet.msbuild
    

    Vous avez besoin de cette étape, car la branche failed-test ne contient pas le travail que vous avez ajouté à la branche unit-tests.

  4. Ajoutez votre fichier de projet de tests et votre fichier manifeste d’outils à l’index de préproduction et commitez vos modifications.

    git add Tailspin.SpaceGame.Web.Tests/Tailspin.SpaceGame.Web.Tests.csproj
    git add .config/dotnet-tools.json
    git commit -m "Configure code coverage tests"
    
  5. Exécutez la commande git push suivante pour charger la branche failed-test sur votre dépôt GitHub :

    git push origin failed-test
    

Voir l’échec du test dans le pipeline

Supposons que vous étiez pressé et que vous avez envoyé votre travail sans exécuter les tests une dernière fois. Heureusement, le pipeline peut vous aider à identifier les problèmes tôt quand il y a des tests unitaires. Vous pouvez voir cela ici.

  1. Dans Azure Pipelines, suivez la build au fil du pipeline.

  2. Développez la tâche Run unit tests - Release quand elle s’exécute.

    Vous pouvez constater que la méthode de test ReturnRequestedCount échoue.

    A screenshot of Azure Pipelines dashboard showing output log of an assertion failure on the unit test, expecting 10 but was 9.

    Le test réussit quand la valeur d’entrée est 0, mais échoue quand elle est 1 ou 10.

    La build n’est publiée dans le pipeline que si la tâche précédente réussit. Ici, la build n’a pas été publiée, car les tests unitaires ont échoué. Ainsi, il est impossible que d’autres personnes obtiennent accidentellement une build endommagée.

Dans la pratique, vous ne suivez pas toujours manuellement la build à mesure qu’elle s’exécute. Voici quelques façons de découvrir l’échec :

  • Une notification par e-mail provenant d’Azure DevOps

    Vous pouvez configurer Azure DevOps pour vous envoyer une notification par e-mail quand la build est terminée. La ligne Objet commence par « [Échec de la build] » quand la build échoue.

    A screenshot of a portion of a build failed email notification.

  • Azure Test Plans

    Dans Azure DevOps, sélectionnez Plans de test, puis sélectionnez Exécutions. Les séries de tests récentes apparaissent, y compris celle que vous venez d’exécuter. Sélectionnez le dernier test terminé. Vous pouvez constater que deux des huit tests ont échoué.

    A screenshot of Azure DevOps test run outcome showing two of eight failed tests as a ring chart.

  • Le tableau de bord

    Dans Azure DevOps, sélectionnez Vue d’ensemble, puis Tableaux de bord. L’échec apparaît dans le widget Tendance des résultats des tests. Le widget Code Coverage est vide, indiquant que la couverture du code n’a pas été exécutée.

    A screenshot of Azure DevOps dashboard trend chart widget showing two failed test in the last test run.

  • Le badge de build

    Bien que la branche failed-test ne comporte pas le badge de build dans README.md, voici ce que vous voyez sur GitHub en cas d’échec de la build :

    A screenshot of Azure Pipelines build badge on GitHub indicating a failure.

Analyser l’échec du test

Quand des tests unitaires échouent, vous avez normalement deux options, selon la nature de l’échec :

  • Si le test révèle un défaut dans le code, corrigez le code et relancez les tests.
  • Si la fonctionnalité change, ajustez le test afin qu’il tienne compte des nouvelles exigences.

Reproduire l’échec localement

Dans cette section, vous allez reproduire la défaillance localement.

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

  2. Dans le terminal, exécutez cette commande dotnet build pour générer l’application :

    dotnet build --configuration Release
    
  3. Dans le terminal, exécutez cette commande dotnet test pour exécuter les tests unitaires :

    dotnet test --no-build --configuration Release
    

    Vous devriez voir les mêmes erreurs que dans le pipeline. Voici une partie de la sortie :

    Starting test execution, please wait...
    A total of 1 test files matched the specified pattern.
      Failed ReturnRequestedCount(1) [33 ms]
      Error Message:
         Expected: 1
      But was:  0
    
      Stack Trace:
         at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context)
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.<>c__DisplayClass1_0.<Execute>b__0()
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.RunTestMethodInThreadAbortSafeZone(TestExecutionContext context, Action action)
    
      Failed ReturnRequestedCount(10) [1 ms]
      Error Message:
         Expected: 10
      But was:  9
    
      Stack Trace:
         at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context)
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.<>c__DisplayClass1_0.<Execute>b__0()
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.RunTestMethodInThreadAbortSafeZone(TestExecutionContext context, Action action)
    
    
    Failed!  - Failed:     2, Passed:     6, Skipped:     0, Total:     8, Duration: 98 ms
    

Rechercher la cause de l’erreur

Vous remarquez que chaque test échoué produit un résultat qui est décalé d'une unité. Par exemple, quand le résultat attendu est 10, le test retourne 9.

Examinez le code source de la méthode testée, LocalDocumentDBRepository<T>.GetItemsAsync. Vous devriez voir ce qui suit :

public Task<IEnumerable<T>> GetItemsAsync(
    Func<T, bool> queryPredicate,
    Func<T, int> orderDescendingPredicate,
    int page = 1, int pageSize = 10
)
{
    var result = _items
        .Where(queryPredicate) // filter
        .OrderByDescending(orderDescendingPredicate) // sort
        .Skip(page * pageSize) // find page
        .Take(pageSize - 1); // take items

    return Task<IEnumerable<T>>.FromResult(result);
}

Dans ce scénario, vous pouvez vérifier GitHub pour voir si le fichier a été récemment modifié.

A screenshot of GitHub showing a file diff where a minus one operation was added.

Vous soupçonnez que pageSize - 1 renvoie un résultat minoré et qu'il ne devrait s'agir que de pageSize. Dans notre scénario, il s'agit d'une erreur que vous avez effectuée lorsque vous avez envoyé (push) un travail sans test. Toutefois, dans un scénario réel, vous pouvez vérifier avec le développeur qui a modifié le fichier sur GitHub pour déterminer la raison de la modification.

Conseil

La discussion et la collaboration peuvent également se produire sur GitHub. Vous pouvez commenter une demande de tirage (pull request) ou ouvrir un problème.

Corriger l’erreur

Dans cette section, vous corrigerez l'erreur en remettant le code dans son état d'origine et en exécutant les tests pour vérifier la correction.

  1. Dans Visual Studio Code, ouvrez Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs depuis l’Explorateur de fichiers.

  2. Modifiez la section GetItemsAsync, comme indiqué ici :

    public Task<IEnumerable<T>> GetItemsAsync(
        Func<T, bool> queryPredicate,
        Func<T, int> orderDescendingPredicate,
        int page = 1, int pageSize = 10
    )
    {
        var result = _items
            .Where(queryPredicate) // filter
            .OrderByDescending(orderDescendingPredicate) // sort
            .Skip(page * pageSize) // find page
            .Take(pageSize); // take items
    
        return Task<IEnumerable<T>>.FromResult(result);
    }
    

    Cette version remplace pageSize - 1 par pageSize.

  3. Enregistrez le fichier .

  4. Dans le terminal intégré, générez l’application.

    dotnet build --configuration Release
    

    Vous devriez voir que le build réussit.

    Dans la pratique, vous pourriez exécuter l’application et l’essayer brièvement. À des fins d’apprentissage, nous allons omettre cette étape pour l’instant.

  5. Dans le terminal, exécutez les tests unitaires.

    dotnet test --no-build --configuration Release
    

    Vous voyez que les tests réussissent.

    Starting test execution, please wait...
    A total of 1 test files matched the specified pattern.
    
    Passed!  - Failed:     0, Passed:     8, Skipped:     0, Total:     8, Duration: 69 ms
    
  6. Dans le terminal intégré, ajoutez chaque fichier modifié à l’index, commitez les modifications, puis poussez la branche vers GitHub.

    git add .
    git commit -m "Return correct number of items"
    git push origin failed-test
    

    Conseil

    Dans cet exemple git add, le point (.) est un caractère générique. Il correspond à tous les fichiers non mis en attente qui se trouvent dans le répertoire actif et dans tous les sous-répertoires.

    Avant d'utiliser ce caractère générique, il est recommandé d'exécuter git status avant de valider pour s'assurer que vous mettez en lots les fichiers concernés.

  7. Retournez à Azure Pipelines. Observez la progression de la modification dans le pipeline. Les tests réussissent, de même que la build globale.

    Si vous le souhaitez, pour vérifier les résultats des tests, vous pouvez sélectionner les onglets Tests et Couverture du code quand la build est terminée.

    Vous pouvez également consulter le tableau de bord pour voir la tendance des résultats mise à jour.

    A screenshot of Azure DevOps dashboard trend chart widget showing a return to all tests passing.

Très bien ! Vous avez corrigé le build. Vous apprendrez ensuite à nettoyer votre environnement Azure DevOps.