Ćwiczenie — dodawanie testów jednostkowych do aplikacji

Ukończone

W tej lekcji dodamy testy jednostkowe do zautomatyzowanej kompilacji utworzonej za pomocą usługi Microsoft Azure Pipelines. Usterki regresji są wkradające się do kodu zespołu i łamiące funkcje filtrowania rankingu. W szczególności pojawia się niewłaściwy tryb gry.

Na poniższej ilustracji przedstawiono problem. Gdy użytkownik wybierze "Milky Way", aby wyświetlić tylko wyniki z tej mapy gry, otrzymuje wyniki z innych map gry, takich jak Andromeda.

Zrzut ekranu przedstawiający ranking pokazujący nieprawidłowe wyniki: Andromeda galaxy scores pokazują na liście galaktyk Drogi Mlecznej.

Zespół chce złapać błąd przed dotarciem do testerów. Testy jednostkowe to doskonały sposób automatycznego testowania pod kątem błędów regresji.

Dodanie testów jednostkowych w tym momencie w procesie zapewni zespołowi początek podczas ulepszania aplikacji internetowej Space Game . Aplikacja używa bazy danych dokumentów do przechowywania wysokich wyników i profilów zawodników. W tej chwili używa ona lokalnych danych testowych. Później planują połączyć aplikację z żywą bazą danych.

Wiele platform testów jednostkowych jest dostępnych dla aplikacji języka C#. Użyjemy narzędzia NUnit, ponieważ jest on popularny w społeczności.

Oto test jednostkowy, z którym pracujesz:

[TestCase("Milky Way")]
[TestCase("Andromeda")]
[TestCase("Pinwheel")]
[TestCase("NGC 1300")]
[TestCase("Messier 82")]
public void FetchOnlyRequestedGameRegion(string gameRegion)
{
    const int PAGE = 0; // take the first page of results
    const int MAX_RESULTS = 10; // sample up to 10 results

    // Form the query predicate.
    // This expression selects all scores for the provided game region.
    Expression<Func<Score, bool>> queryPredicate = score => (score.GameRegion == gameRegion);

    // Fetch the scores.
    Task<IEnumerable<Score>> scoresTask = _scoreRepository.GetItemsAsync(
        queryPredicate, // the predicate defined above
        score => 1, // we don't care about the order
        PAGE,
        MAX_RESULTS
    );
    IEnumerable<Score> scores = scoresTask.Result;

    // Verify that each score's game region matches the provided game region.
    Assert.That(scores, Is.All.Matches<Score>(score => score.GameRegion == gameRegion));
}

Ranking można filtrować według dowolnej kombinacji typu gry i mapy gier.

Ten test wysyła zapytanie do rankingu pod kątem wysokich wyników i sprawdza, czy każdy wynik jest zgodny z podaną mapą gry.

W metodzie TestCase testowej NUnit dane wbudowane są używane do testowania tej metody. W tym miejscu NUnit wywołuje metodę testu jednostkowego FetchOnlyRequestedGameRegion w następujący sposób:

FetchOnlyRequestedGameRegion("Milky Way");
FetchOnlyRequestedGameRegion("Andromeda");
FetchOnlyRequestedGameRegion("Pinwheel");
FetchOnlyRequestedGameRegion("NGC 1300");
FetchOnlyRequestedGameRegion("Messier 82");

Zwróć uwagę na Assert.That wywołanie metody na końcu testu. Potwierdzenie jest warunkiem lub instrukcją, którą deklarujesz jako true. Jeśli warunek okaże się fałszywy, może to oznaczać usterkę w kodzie. Narzędzie NUnit uruchamia każdą metodę testową przy użyciu wbudowanych danych, które określisz, i rejestruje wynik w wyniku testu zakończonego niepowodzeniem lub testu zakończonego niepowodzeniem.

Wiele struktur testów jednostkowych zapewnia metody weryfikacji podobne do języka naturalnego. Te metody ułatwiają odczytywanie testów i pomaga mapować testy na wymagania aplikacji.

Rozważ potwierdzenie dokonane w tym przykładzie:

Assert.That(scores, Is.All.Matches<Score>(score => score.GameRegion == gameRegion));

Ten wiersz może być odczytywany jako:

Twierdzenie, że region gry każdego zwróconego wyniku jest zgodny z podanym regionem gry.

Oto proces, który należy wykonać:

  1. Pobierz gałąź z repozytorium GitHub zawierającego testy jednostkowe.
  2. Uruchom testy lokalnie, aby sprawdzić, czy przechodzą.
  3. Dodaj zadania do konfiguracji potoku, aby uruchomić testy i zebrać wyniki.
  4. Wypchnij gałąź do repozytorium GitHub.
  5. Obejrzyj projekt usługi Azure Pipelines automatycznie skompiluj aplikację i uruchom testy.

Pobieranie gałęzi z repozytorium GitHub

W tym miejscu pobierzesz unit-tests gałąź z usługi GitHub i wyewidencjonujesz lub przełączysz się do tej gałęzi.

Ta gałąź zawiera projekt Space Game , z którym pracowaliśmy w poprzednich modułach, oraz konfigurację usługi Azure Pipelines do rozpoczęcia od.

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

  2. Uruchom następujące git polecenia, aby pobrać gałąź o nazwie unit-tests z repozytorium Firmy Microsoft, a następnie przełączyć się do tej gałęzi.

    git fetch upstream unit-tests
    git checkout -B unit-tests upstream/unit-tests
    

    Format tego polecenia umożliwia pobranie kodu początkowego z repozytorium Microsoft GitHub, znanego jako upstream. Wkrótce wypchniesz tę gałąź do repozytorium GitHub o nazwie origin.

  3. Opcjonalnie otwórz plik azure-pipelines.yml w programie Visual Studio Code i zapoznaj się z początkową konfiguracją. Konfiguracja ta przypomina konfigurację podstawową utworzoną w module Tworzenie potoku kompilacji za pomocą usługi Azure Pipelines. Tworzona jest tylko konfiguracja wydania aplikacji.

Uruchamianie testów lokalnie

Warto uruchomić wszystkie testy lokalnie przed przesłaniem testów do potoku. Dodasz ten wskaźnik tutaj.

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

  2. Uruchom polecenie dotnet build , aby skompilować każdy projekt w rozwiązaniu.

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

    dotnet test --configuration Release --no-build
    

    Flaga --no-build określa, że nie należy kompilować projektu przed jego uruchomieniem. Nie musisz kompilować projektu, ponieważ został utworzony w poprzednim kroku.

    Powinny zostać wyświetlone wszystkie pięć testów z powodzeniem.

    Starting test execution, please wait...
    A total of 1 test files matched the specified pattern.
    
    Passed!  - Failed:     0, Passed:     5, Skipped:     0, Total:     5, Duration: 57 ms
    

    W tym przykładzie testy miały mniej niż jedną sekundę do uruchomienia.

    Zwróć uwagę, że było pięć testów całkowitych. Mimo że zdefiniowaliśmy tylko jedną metodę testową, FetchOnlyRequestedGameRegiontest jest uruchamiany pięć razy, raz dla każdej mapy gry, jak określono w danych wbudowanych TestCase .

  4. Uruchom testy po raz drugi. Tym razem podaj --logger opcję zapisu wyników w pliku dziennika.

    dotnet test Tailspin.SpaceGame.Web.Tests --configuration Release --no-build --logger trx
    

    Zobaczysz z danych wyjściowych, że plik TRX jest tworzony w katalogu TestResults .

    Plik TRX to dokument XML zawierający wyniki przebiegu testu. Jest to popularny format wyników testów, ponieważ program Visual Studio i inne narzędzia mogą ułatwić wizualizowanie wyników.

    Później zobaczysz, jak usługa Azure Pipelines może ułatwić wizualizowanie i śledzenie wyników testów podczas ich uruchamiania w potoku.

    Uwaga

    Pliki TRX nie powinny być uwzględniane w kontroli źródła. Plik .gitignore umożliwia określenie plików tymczasowych i innych, które mają być ignorowane przez usługę Git. Plik gitignore projektu jest już skonfigurowany do ignorowania wszystkich elementów w katalogu TestResults.

  5. Opcjonalnie w programie Visual Studio Code otwórz plik DocumentDBRepository_GetItemsAsyncShould.cs z folderu Tailspin.SpaceGame.Web.Tests i sprawdź kod testowy. Nawet jeśli nie interesuje Cię tworzenie aplikacji platformy .NET, możesz znaleźć przydatny kod testowy, ponieważ przypomina kod, który może być widoczny w innych strukturach testów jednostkowych.

Dodawanie zadań do konfiguracji potoku

W tym miejscu skonfigurujesz potok kompilacji, aby uruchamiać testy jednostkowe i zbierać wyniki.

  1. W programie Visual Studio Code zmodyfikuj azure-pipelines.yml w następujący sposób:

    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: 'Run unit tests - $(buildConfiguration)'
      inputs:
        command: 'test'
        arguments: '--no-build --configuration $(buildConfiguration)'
        publishTestResults: true
        projects: '**/*.Tests.csproj'
    
    - 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()
    

    Ta wersja wprowadza to DotNetCoreCLI@2 zadanie kompilacji.

    - task: DotNetCoreCLI@2
      displayName: 'Run unit tests - $(buildConfiguration)'
      inputs:
        command: 'test'
        arguments: '--no-build --configuration $(buildConfiguration)'
        publishTestResults: true
        projects: '**/*.Tests.csproj'
    

    To zadanie kompilacji dotnet test uruchamia polecenie .

    Zwróć uwagę, że to zadanie nie określa argumentu użytego --logger trx podczas ręcznego uruchamiania testów. Argument publishTestResults dodaje to za Ciebie. Ten argument informuje potok o wygenerowaniu pliku TRX do katalogu tymczasowego dostępnego za pośrednictwem wbudowanej $(Agent.TempDirectory) zmiennej. Publikuje również wyniki zadania w potoku.

    Argument projects określa wszystkie projekty języka C#, które pasują do "**/*". Tests.csproj". Część "**" pasuje do wszystkich katalogów i "*. Część tests.csproj" pasuje do wszystkich projektów, których nazwa pliku kończy się ciągiem ". Tests.csproj". Gałąź unit-tests zawiera tylko jeden projekt testu jednostkowego Tailspin.SpaceGame.Web.Tests.csproj. Określając wzorzec, można uruchamiać więcej projektów testowych bez konieczności modyfikowania konfiguracji kompilacji.

Wypychanie gałęzi do usługi GitHub

W tym miejscu wypchniesz zmiany do usługi GitHub i zobaczysz przebieg potoku. Pamiętaj, że jesteś obecnie w gałęzi unit-tests.

  1. W zintegrowanym terminalu dodaj azure-pipelines.yml do indeksu, zatwierdź zmiany i wypchnij gałąź do usługi GitHub.

    git add azure-pipelines.yml
    git commit -m "Run and publish unit tests"
    git push origin unit-tests
    

Obejrzyj, jak usługa Azure Pipelines uruchamia testy

W tym miejscu zostaną wyświetlone testy uruchomione w potoku, a następnie zwizualizuj wyniki z planów testów platformy Microsoft Azure. Plany testów platformy Azure udostępniają wszystkie narzędzia potrzebne do pomyślnego przetestowania aplikacji. Możesz tworzyć i uruchamiać ręczne plany testów, generować testy automatyczne i zbierać opinie osób biorących udział w projekcie.

  1. W usłudze Azure Pipelines prześledzić kompilację za pomocą każdego z kroków.

    Zobaczysz, że zadanie Uruchom testy jednostkowe — wydanie uruchamia testy jednostkowe tak samo jak ręcznie z wiersza polecenia.

    Zrzut ekranu usługi Azure Pipelines przedstawiający dane wyjściowe konsoli z uruchamiania testów jednostkowych.

  2. Wróć do podsumowania potoku.

  3. Przejdź do karty Testy .

    Zostanie wyświetlone podsumowanie przebiegu testu. Wszystkie pięć testów zakończyło się pomyślnie.

    Zrzut ekranu usługi Azure Pipelines przedstawiający kartę Testy z 5 łącznymi przebiegami testów i przekazywaniem 100%.

  4. W usłudze Azure DevOps wybierz pozycję Plany testów, a następnie wybierz pozycję Uruchomienia.

    Zrzut ekranu przedstawiający menu nawigacji usługi Azure DevOps z wyróżnioną sekcją Plany testów i kartą Uruchomienia.

    Zobaczysz najnowsze przebiegi testów, w tym te, które właśnie uruchomiono.

  5. Kliknij dwukrotnie najnowszy przebieg testu.

    Zostanie wyświetlone podsumowanie wyników.

    Zrzut ekranu przedstawiający podsumowanie wyników przebiegu testu usługi Azure DevOps z 5 pomyślnie przeprowadzonymi testami.

    W tym przykładzie wszystkie pięć testów zakończyło się pomyślnie. Jeśli jakiekolwiek testy nie powiodły się, możesz przejść do zadania kompilacji, aby uzyskać więcej szczegółów.

    Możesz również pobrać plik TRX, aby sprawdzić go za pomocą programu Visual Studio lub innego narzędzia do wizualizacji.

Mimo że dodano tylko jeden test, jest to dobry początek i rozwiązuje on bezpośredni problem. Teraz zespół ma miejsce, aby dodać więcej testów i uruchomić je w miarę ulepszania procesu.

Scal gałąź z gałęzią główną

W rzeczywistym scenariuszu, jeśli wyniki są zadowoleni, możesz scalić unit-tests gałąź z maingałęzią , ale w celu zwięzłości pominiemy ten proces na razie.