Cvičení – oprava selhání testu

Dokončeno

V této chvíli máte možnost spouštět testy jednotek tak, jak kanálem buildu procházejí změny. Máte také způsob, jak můžete měřit množství kódu, který pokrývají vaše testy.

Vždy je vhodné spouštět testy místně před odesláním změn do kanálu. Co se ale stane, když někdo zapomene a odešle změnu, která způsobí selhání sestavení?

V této lekci opravíte poškozené sestavení, které je způsobeno neúspěšným testem jednotek. V této části provedete tyto akce:

  • Získejte počáteční kód z GitHubu.
  • Přidejte do projektu nástroje pro pokrytí kódu.
  • Nasdílíte tento kód do vašeho úložiště.
  • Budete sledovat automatické spuštění kanálu a selhání testu jednotek.
  • Chybu reprodukujete místně.
  • Zanalyzujete a opravíte selhání.
  • Nasdílíte opravu a budete sledovat úspěšné vytvoření sestavení.

Revize nového testu jednotek

Nejnovější funkce týmu zahrnuje tabulku výsledků. Potřebujeme získat počet skóre z databáze, abychom mohli napsat test jednotek pro ověření IDocumentDBRepository<T>.GetItemsAsync metody.

Test vypadá takto. Zatím nemusíte přidávat žádný kód.

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

Připomínáme, že v testu nástroje NUnit poskytuje vložená data k otestování této metody funkce TestCase. NUnit volá metodu ReturnRequestedCount pro test jednotek takto:

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

Tento test používá také vlastnost ExpectedResult ke zjednodušení testovacího kódu a k tomu, aby byl jasný záměr testu. NUnit automaticky porovnává vrácenou hodnotu s hodnotou této vlastnosti, takže není potřeba explicitně volat kontrolní výraz.

Zvolíme několik hodnot, které představují typické dotazy. Zahrneme také 0, abychom tento hraniční případ pokryli.

Načtení větve z GitHubu

Jak jste to udělali dříve, načtěte větev z GitHubu failed-test a podívejte se (nebo přepněte na) tuto větev.

  1. V editoru Visual Studio Code otevřete integrovaný terminál.

  2. Spuštěním následujících git fetch příkazů git checkout stáhněte větev pojmenovanou failed-test z úložiště Microsoftu a přepněte na tuto větev:

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

    Větev failed-test jsme pojmenovali pro účely výuky. V praxi byste větev pojmenovala za jejím účelem nebo funkcí.

  3. Spuštěním těchto příkazů vytvořte soubor manifestu místního nástroje, nainstalujte ReportGenerator nástroj a přidejte coverlet.msbuild balíček do projektu testů:

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

    Tento krok potřebujete, protože failed-test větev neobsahuje práci, kterou jste přidali do unit-tests větve.

  4. Přidejte soubor testovacího projektu a soubor manifestu nástroje do přípravného indexu a potvrďte změny.

    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. Spuštěním následujícího git push příkazu nahrajte failed-test větev do úložiště GitHub:

    git push origin failed-test
    

Selhání testu v kanálu

Řekněme, že jste byli ve spěchu a vysadili svou práci bez toho, abyste testy spustili jednou do posledního okamžiku. Kanál vám naštěstí může pomoct zachytit problémy v rané fázi, když dojde k testům jednotek. Vidíte to tady.

  1. V Azure Pipelines sledujte sestavení, jak prochází kanálem.

  2. Rozbalte spuštěnou úlohu Run unit tests - Release (Spuštění testů jednotek – vydání).

    Uvidíte, že testovací metoda ReturnRequestedCount selže.

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

    Test projde, když je vstupní hodnota 0, ale selže, když je vstupní hodnota 1 nebo 10.

    Sestavení se publikuje do kanálu jen v případě, že je předchozí úloha úspěšná. Sestavení se zde nepublikovalo, protože testy jednotek selhaly. To zabrání tomu, aby ostatní dostali sestavení s chybami.

V praxi nebudete vždycky manuálně sledovat spouštění sestavení. Tady je několik způsobů, jak můžete zjistit selhání:

  • E-mailové oznámení z Azure DevOps

    Azure DevOps můžete nakonfigurovat tak, aby vám po dokončení sestavení poslalo e-mailové oznámení. Pokud sestavení selže, začíná řádek předmětu označením „Build failed“ (Sestavení selhalo).

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

  • Azure Test Plans

    V Azure DevOps vyberte Testovací plány a pak vyberte Spustit. Zobrazí se vám nedávná spuštění testů včetně toho, který právě proběhl. Vyberte nejnovější dokončený test. Vidíte, že dva z osmi testů selhaly.

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

  • Řídicí panel

    V Azure DevOps vyberte Přehled a pak vyberte Řídicí panely. Ve widgetu Test Results Trend (Trend výsledků testů) se vám zobrazí selhání testu. Widget Pokrytí kódu je prázdný, což znamená, že pokrytí kódu nebylo spuštěno.

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

  • Odznáček buildu

    failed-test I když větev do souboru README.md neobsahuje odznáček buildu, uvidíte na GitHubu, když se sestavení nezdaří:

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

Analýza selhání testu

Pokud testy jednotek selžou, obvykle máte dvě možnosti v závislosti na povaze selhání:

  • Pokud test odhalí chybu v kódu, opravte kód a znovu spusťte testy.
  • Pokud se funkce změní, upravte test tak, aby odpovídal novým požadavkům.

Reprodukování selhání místně

V této části reprodukujete selhání místně.

  1. V editoru Visual Studio Code otevřete integrovaný terminál.

  2. V terminálu spusťte tento dotnet build příkaz pro sestavení aplikace:

    dotnet build --configuration Release
    
  3. V terminálu spusťte tento dotnet test příkaz a spusťte testy jednotek:

    dotnet test --no-build --configuration Release
    

    Měly by se zobrazit stejné chyby, které jste viděli v kanálu. Tady je část výstupu:

    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
    

Zjištění příčiny chyby

Všimněte si, že každý neúspěšný test vytvoří výsledek, který je o jeden vypnutý. Pokud se například očekává hodnota 10, vrátí test hodnotu 9.

Podívejte se na zdrojový kód pro otestovanou LocalDocumentDBRepository<T>.GetItemsAsyncmetodu . Měli byste vidět toto:

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

V tomto scénáři můžete zkontrolovat GitHub a zjistit, jestli se soubor nedávno změnil.

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

Máte podezření, že pageSize - 1 vrací jeden méně výsledků a že by to mělo být jen pageSize. V našem scénáři se jedná o chybu, kterou jste provedli, když jste odeslali práci bez testování, ale v reálném scénáři byste mohli zjistit příčinu změny u vývojáře, který soubor na GitHubu změnil.

Tip

V GitHubu je také možné diskutovat a spolupracovat. Můžete okomentovat žádost o přijetí změn nebo otevřít nějaký problém.

Oprava chyby

V této části chybu opravíte tak, že kód změníte zpět do původního stavu a spustíte testy a ověříte opravu.

  1. V editoru Visual Studio Code otevřete soubor Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs z průzkumníka souborů.

  2. Upravte metodu GetItemsAsync , jak je znázorněno zde:

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

    Tato verze změní pageSize - 1 na pageSize.

  3. Soubor uložte.

  4. V integrovaném terminálu sestavte aplikaci.

    dotnet build --configuration Release
    

    Měli byste vidět, že sestavení bude úspěšné.

    V praxi můžete aplikaci spustit a krátce ji vyzkoušet. Pro účely výuky to prozatím přeskočíme.

  5. V terminálu spusťte testy jednotek.

    dotnet test --no-build --configuration Release
    

    Uvidíte, že testy budou úspěšné.

    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. V integrovaném terminálu přidejte každý upravený soubor do indexu, potvrďte změny a nasdílejte větev do GitHubu.

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

    Tip

    Tečka (.) v tomto git add příkladu je zástupný znak. Odpovídá všem souborům v aktuálním adresáři a všech podadresářích, které nejsou v přípravném prostředí.

    Před použitím tohoto zástupné znaky je vhodné spustit git status před potvrzením, abyste zajistili, že připravíte soubory, které chcete provést.

  7. Vraťte se do Azure Pipelines. Sledujte, jak změna prochází kanálem. Testy projdou a celkové sestavení bude úspěšné.

    Pokud chcete ověřit výsledky testu, můžete po dokončení sestavení vybrat karty Testy a pokrytí kódu.

    Můžete se také podívat na řídicí panel a zobrazit aktualizovaný trend výsledků.

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

Výborně! Opravili jste sestavení. Dále se dozvíte, jak vyčistit prostředí Azure DevOps.