Övning – Åtgärda ett misslyckat test

Slutförd

Nu kan du köra enhetstester när ändringarna flyttas genom bygg-pipelinen. Du kan också mäta mängden kod som omfattas av dina tester.

Det är alltid en bra idé att köra dina tester lokalt innan du skickar ändringar till pipelinen. Men vad händer när någon glömmer och skickar in en ändring som bryter bygget?

I den här lektionen ska du åtgärda en trasig version som orsakas av ett misslyckat enhetstest. Här gör du följande:

  • Hämta startkod från GitHub.
  • Lägg till verktyg för kodtäckning i projektet.
  • Skicka koden till lagringsplatsen.
  • Se hur pipelinen körs automatiskt och enhetstesterna misslyckas.
  • Återskapa felet lokalt.
  • Analysera och åtgärda felet.
  • Push-överför en korrigering och se hur bygget lyckas.

Granska det nya enhetstestet

Teamets senaste funktion omfattar rankningslistan. Vi måste hämta antalet poäng från databasen, så att vi kan skriva ett enhetstest för att verifiera IDocumentDBRepository<T>.GetItemsAsync metoden.

Så här ser testet ut. Du behöver inte lägga till någon kod än.

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

Kom ihåg att i ett NUnit-test TestCase innehåller infogade data att använda för att testa den metoden. NUnit anropar ReturnRequestedCount enhetstestmetoden så här:

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

Det här testet använder ExpectedResult också egenskapen för att förenkla testkoden och göra avsikten tydlig. NUnit jämför automatiskt returvärdet med värdet för den här egenskapen, vilket tar bort behovet av att uttryckligen anropa försäkran.

Vi väljer några värden som representerar vanliga frågor. Vi inkluderar även 0 för att täcka det kantfallet.

Hämta grenen från GitHub

Som du gjorde tidigare hämtar du grenen failed-test från GitHub och checkar ut (eller växlar till) den grenen.

  1. Öppna den integrerade terminalen i Visual Studio Code.

  2. Kör följande git fetch kommandon och git checkout för att ladda ned en gren med namnet failed-test från Microsoft-lagringsplatsen och växla till den grenen:

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

    Vi namngav grenen failed-test i utbildningssyfte. I praktiken namnger du en gren efter dess syfte eller funktion.

  3. Kör dessa kommandon för att skapa en manifestfil för lokala verktyg, installera ReportGenerator verktyget och lägg till paketet i coverlet.msbuild testprojektet:

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

    Du behöver det här steget eftersom grenen failed-test inte innehåller det arbete som du har lagt till i grenen unit-tests .

  4. Lägg till testprojektfilen och verktygsmanifestfilen i mellanlagringsindexet och genomför ändringarna.

    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. Kör följande git push kommando för att ladda upp grenen failed-test till din GitHub-lagringsplats:

    git push origin failed-test
    

Se testfelet i pipelinen

Låt oss säga att du hade bråttom och sköt upp ditt arbete utan att köra testerna en sista gång. Som tur är kan pipelinen hjälpa dig att fånga upp problem tidigt när det finns enhetstester. Du kan se det här.

  1. I Azure Pipelines spårar du bygget när det körs via pipelinen.

  2. Expandera körningsenhetens tester – Släpp aktiviteten medan den körs.

    Du ser att ReturnRequestedCount testmetoden misslyckas.

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

    Testet godkänns när indatavärdet är 0, men det misslyckas när indatavärdet är 1 eller 10.

    Bygget publiceras endast till pipelinen när den tidigare uppgiften lyckas. Här publicerades inte bygget eftersom enhetstesterna misslyckades. Detta hindrar andra från att oavsiktligt få en trasig version.

I praktiken spårar du inte alltid bygget manuellt när det körs. Här följer några sätt att upptäcka felet:

  • Ett e-postmeddelande från Azure DevOps

    Du kan konfigurera Azure DevOps så att du får ett e-postmeddelande när bygget är klart. Ämnesraden börjar med "[Build failed]" när bygget misslyckas.

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

  • Azure-testplaner

    I Azure DevOps väljer du Testplaner och sedan Körningar. Du ser de senaste testkörningarna, inklusive den som just kördes. Välj det senaste slutförda testet. Du ser att två av de åtta testerna misslyckades.

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

  • Instrumentpanelen

    I Azure DevOps väljer du Översikt och sedan Instrumentpaneler. Felet visas i widgeten Trend för testresultat . Widgeten Kodtäckning är tom, vilket indikerar att kodtäckningen inte kördes.

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

  • Byggmärket

    Även om grenen failed-test inte innehåller byggmärket i README.md-filen visas det här på GitHub när bygget misslyckas:

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

Analysera testfelet

När enhetstester misslyckas har du vanligtvis två alternativ, beroende på typen av fel:

  • Om testet visar en defekt i koden kan du åtgärda koden och köra testerna igen.
  • Om funktionen ändras justerar du testet så att det matchar de nya kraven.

Återskapa felet lokalt

I det här avsnittet återskapar du felet lokalt.

  1. Öppna den integrerade terminalen i Visual Studio Code.

  2. I terminalen kör du det här dotnet build kommandot för att skapa programmet:

    dotnet build --configuration Release
    
  3. Kör det här dotnet test kommandot i terminalen för att köra enhetstesterna:

    dotnet test --no-build --configuration Release
    

    Du bör se samma fel som du såg i pipelinen. Här är en del av utdata:

    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
    

Hitta orsaken till felet

Du märker att varje misslyckat test ger ett resultat som är inaktiverat med ett. När till exempel 10 förväntas returnerar testet 9.

Ta en titt på källkoden för den metod som testas, LocalDocumentDBRepository<T>.GetItemsAsync. Du bör se följande:

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

I det här scenariot kan du kontrollera GitHub för att se om filen nyligen har ändrats.

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

Du misstänker att pageSize - 1 returnerar ett resultat färre och att detta bara ska vara pageSize. I vårt scenario är det här ett fel som du gjorde när du push-överförde arbete utan testning, men i ett verkligt scenario kan du kontrollera med utvecklaren som ändrade filen på GitHub för att fastställa orsaken till ändringen.

Dricks

Diskussioner och samarbete kan också ske på GitHub. Du kan kommentera en pull-begäran eller öppna ett problem.

Åtgärda felet

I det här avsnittet åtgärdar du felet genom att ändra tillbaka koden till dess ursprungliga tillstånd och köra testerna för att verifiera korrigeringen.

  1. I Visual Studio Code öppnar du Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs från utforskaren.

  2. GetItemsAsync Ändra metoden enligt följande:

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

    Den här versionen ändras pageSize - 1 till pageSize.

  3. Spara filen.

  4. Skapa programmet i den integrerade terminalen.

    dotnet build --configuration Release
    

    Du bör se att bygget lyckas.

    I praktiken kan du köra appen och kort prova den. I utbildningssyfte hoppar vi över det för tillfället.

  5. Kör enhetstesterna i terminalen.

    dotnet test --no-build --configuration Release
    

    Du ser att testerna godkänns.

    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. I den integrerade terminalen lägger du till varje modifierad fil i indexet, checkar in ändringarna och push-överför grenen till GitHub.

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

    Dricks

    Punkten (.) i det här git add exemplet är ett jokertecken. Den matchar alla otaglade filer i den aktuella katalogen och alla underkataloger.

    Innan du använder det här jokertecknet är det en bra idé att köra git status innan du checkar in för att säkerställa att du mellanlagra de filer som du tänker mellanlagra.

  7. Gå tillbaka till Azure Pipelines. Se hur ändringen flyttas genom pipelinen. Testerna godkänns och den övergripande versionen lyckas.

    Om du vill verifiera testresultaten kan du välja flikarna Test och Kodtäckning när bygget är klart.

    Du kan också kolla in instrumentpanelen för att visa den uppdaterade resultattrenden.

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

Toppen! Du har fixat bygget. Nu får du lära dig hur du rensar din Azure DevOps-miljö.