Ćwiczenie — naprawianie testu, który zakończył się niepowodzeniem

Ukończone

W tym momencie masz sposób uruchamiania testów jednostkowych w miarę przechodzenia zmian przez potok kompilacji. Istnieje również sposób mierzenia ilości kodu objętego testami.

Zawsze dobrym pomysłem jest uruchamianie testów lokalnie przed przesłaniem zmian do potoku. Ale co się stanie, gdy ktoś zapomni i prześle zmianę, która łamie kompilację?

W tej lekcji naprawisz uszkodzoną kompilację, która jest spowodowana niepowodzeniem testu jednostkowego. Tutaj wykonasz następujące zadania:

  • Pobierz kod początkowy z usługi GitHub.
  • Dodaj narzędzia pokrycia kodu do projektu.
  • Wypchnij kod do repozytorium.
  • Obserwuj automatyczne uruchomienie potoku i testy jednostkowe kończą się niepowodzeniem.
  • Odtwórz błąd lokalnie.
  • Analizowanie i naprawianie błędu.
  • Wypchnij poprawkę i obserwuj powodzenie kompilacji.

Przeglądanie nowego testu jednostkowego

Najnowsza funkcja zespołu obejmuje ranking. Musimy uzyskać liczbę wyników z bazy danych, aby można było napisać test jednostkowy w celu zweryfikowania IDocumentDBRepository<T>.GetItemsAsync metody.

Oto jak wygląda test. Nie musisz jeszcze dodawać żadnego kodu.

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

Pamiętaj, TestCase że w teście NUnit dane wbudowane są używane do testowania tej metody. NUnit wywołuje metodę testu jednostkowego ReturnRequestedCount w następujący sposób:

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

Ten test używa ExpectedResult również właściwości , aby uprościć kod testowy i pomóc w wyczyszczeniu intencji. NUnit automatycznie porównuje wartość zwracaną z wartością tej właściwości, usuwając konieczność jawnego wywołania asercji.

Wybierzemy kilka wartości reprezentujących typowe zapytania. Uwzględnimy również 0, aby pokryć ten przypadek krawędzi.

Pobieranie gałęzi z repozytorium GitHub

Tak jak wcześniej, pobierz failed-test gałąź z usługi GitHub i wyewidencjonuj (lub przełącz się do) tej gałęzi.

  1. W programie Visual Studio Code otwórz zintegrowany terminal.

  2. Uruchom następujące git fetch polecenia i git checkout , aby pobrać gałąź o nazwie failed-test z repozytorium Firmy Microsoft i przełączyć się do tej gałęzi:

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

    Nazwaliśmy gałąź failed-test do celów szkoleniowych. W praktyce należy nazwać gałąź po jej celu lub funkcji.

  3. Uruchom następujące polecenia, aby utworzyć lokalny plik manifestu narzędzia, zainstalować ReportGenerator narzędzie i dodać coverlet.msbuild pakiet do projektu testów:

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

    Ten krok jest potrzebny, ponieważ failed-test gałąź nie zawiera pracy dodanej do unit-tests gałęzi.

  4. Dodaj plik projektu testowego i plik manifestu narzędzia do indeksu przejściowego i zatwierdź zmiany.

    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. Uruchom następujące git push polecenie, aby przekazać failed-test gałąź do repozytorium GitHub:

    git push origin failed-test
    

Zobacz błąd testu w potoku

Załóżmy, że spieszyłeś się i wypchnął swoją pracę bez przeprowadzania testów po raz ostatni. Na szczęście potok może pomóc w przechwytywaniu problemów na wczesnym etapie, gdy istnieją testy jednostkowe. Widać to tutaj.

  1. W usłudze Azure Pipelines śledź kompilację, gdy jest ona uruchamiana przez potok.

  2. Rozwiń zadanie Uruchom testy jednostkowe — wydanie , gdy jest uruchamiane.

    Zobaczysz, że metoda testowa kończy się niepowodzeniem ReturnRequestedCount .

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

    Test kończy się powodzeniem, gdy wartość wejściowa wynosi 0, ale kończy się niepowodzeniem, gdy wartość wejściowa to 1 lub 10.

    Kompilacja jest publikowana w potoku tylko wtedy, gdy poprzednie zadanie zakończy się pomyślnie. W tym miejscu kompilacja nie została opublikowana, ponieważ testy jednostkowe nie powiodły się. Zapobiega to przypadkowemu uzyskaniu uszkodzonej kompilacji przez inne osoby.

W praktyce nie zawsze będzie można ręcznie śledzić kompilację podczas jej uruchamiania. Oto kilka sposobów wykrycia błędu:

  • Powiadomienie e-mail z usługi Azure DevOps

    Możesz skonfigurować usługę Azure DevOps, aby wysłać powiadomienie e-mail po zakończeniu kompilacji. Wiersz tematu rozpoczyna się od ciągu "[Kompilacja nie powiodła się]", gdy kompilacja zakończy się niepowodzeniem.

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

  • Plany testów platformy Azure

    W usłudze Azure DevOps wybierz pozycję Plany testów, a następnie wybierz pozycję Uruchomienia. Zobaczysz ostatnie przebiegi testów, w tym te, które właśnie uruchomiono. Wybierz najnowszy ukończony test. Zobaczysz, że dwa z ośmiu testów zakończyły się niepowodzeniem.

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

  • Pulpit nawigacyjny

    W usłudze Azure DevOps wybierz pozycję Przegląd, a następnie wybierz pozycję Pulpity nawigacyjne. Zostanie wyświetlony błąd w widżecie Trend wyników testów. Widżet Pokrycie kodu jest pusty, co oznacza, że pokrycie kodu nie zostało uruchomione.

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

  • Znaczek kompilacji

    failed-test Mimo że gałąź nie zawiera wskaźnika kompilacji w pliku README.md, oto co można zobaczyć w usłudze GitHub, gdy kompilacja zakończy się niepowodzeniem:

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

Analizowanie niepowodzenia testu

Gdy testy jednostkowe kończą się niepowodzeniem, zwykle masz dwie opcje, w zależności od charakteru błędu:

  • Jeśli test ujawni usterkę w kodzie, napraw kod i uruchom ponownie testy.
  • Jeśli funkcjonalność ulegnie zmianie, dostosuj test, aby był zgodny z nowymi wymaganiami.

Odtwórz błąd lokalnie

W tej sekcji odtworzysz błąd lokalnie.

  1. W programie Visual Studio Code otwórz zintegrowany terminal.

  2. W terminalu uruchom następujące dotnet build polecenie, aby skompilować aplikację:

    dotnet build --configuration Release
    
  3. W terminalu uruchom następujące dotnet test polecenie, aby uruchomić testy jednostkowe:

    dotnet test --no-build --configuration Release
    

    W potoku powinny zostać wyświetlone te same błędy, które wystąpiły w potoku. Oto część danych wyjściowych:

    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
    

Znajdowanie przyczyny błędu

Zauważysz, że każdy test, który zakończył się niepowodzeniem, generuje wynik wyłączony przez jeden. Na przykład gdy oczekiwano wartości 10, test zwraca wartość 9.

Zapoznaj się z kodem źródłowym metody, która jest testowana, LocalDocumentDBRepository<T>.GetItemsAsync. Powinien zostać wyświetlony następujący komunikat:

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

W tym scenariuszu możesz sprawdzić usługę GitHub, aby sprawdzić, czy plik został niedawno zmieniony.

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

Podejrzewasz, że pageSize - 1 zwraca jeden mniej wyników i że powinno to być tylko pageSize. W naszym scenariuszu jest to błąd, który wystąpił podczas wypychania pracy bez testowania, ale w rzeczywistym scenariuszu możesz sprawdzić u dewelopera, który zmienił plik w usłudze GitHub, aby określić przyczynę zmiany.

Napiwek

Dyskusje i współpraca mogą również odbywać się w usłudze GitHub. Możesz skomentować żądanie ściągnięcia lub otworzyć problem.

Naprawianie błędu

W tej sekcji naprawisz błąd, zmieniając kod z powrotem na jego oryginalny stan i uruchamiając testy w celu zweryfikowania poprawki.

  1. W programie Visual Studio Code otwórz plik Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs z poziomu eksploratora plików.

  2. Zmodyfikuj metodę GetItemsAsync , jak pokazano poniżej:

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

    Ta wersja zmienia się pageSize - 1 na pageSize.

  3. Zapisz plik.

  4. W zintegrowanym terminalu skompiluj aplikację.

    dotnet build --configuration Release
    

    Powinien zostać wyświetlony komunikat o pomyślnym zakończeniu kompilacji.

    W praktyce możesz uruchomić aplikację i pokrótce wypróbować ją. W celach szkoleniowych pominiemy to na razie.

  5. W terminalu uruchom testy jednostkowe.

    dotnet test --no-build --configuration Release
    

    Zobaczysz, że testy przechodzą pomyślnie.

    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. W zintegrowanym terminalu dodaj każdy zmodyfikowany plik do indeksu, zatwierdź zmiany i wypchnij gałąź do usługi GitHub.

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

    Napiwek

    Kropka (.) w tym git add przykładzie jest symbolem wieloznacznymi. Pasuje do wszystkich nieprzytagowanych plików w bieżącym katalogu i wszystkich podkatalogach.

    Przed użyciem tego symbolu wieloznacznych dobrym rozwiązaniem jest uruchomienie git status przed zatwierdzeniem w celu upewnienia się, że pliki, które zamierzasz przygotować.

  7. Wróć do usługi Azure Pipelines. Obejrzyj zmianę przechodzącą przez potok. Testy kończą się powodzeniem, a ogólna kompilacja zakończy się powodzeniem.

    Opcjonalnie, aby zweryfikować wyniki testu, możesz wybrać karty Testy i pokrycie kodu po zakończeniu kompilacji.

    Możesz również sprawdzić pulpit nawigacyjny, aby wyświetlić zaktualizowany trend wyników.

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

Świetnie! Naprawiono kompilację. Następnie dowiesz się, jak wyczyścić środowisko usługi Azure DevOps.