Ćwiczenie — dodawanie testów jednostkowych do aplikacji
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.
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ć:
- Pobierz gałąź z repozytorium GitHub zawierającego testy jednostkowe.
- Uruchom testy lokalnie, aby sprawdzić, czy przechodzą.
- Dodaj zadania do konfiguracji potoku, aby uruchomić testy i zebrać wyniki.
- Wypchnij gałąź do repozytorium GitHub.
- 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.
W programie Visual Studio Code otwórz zintegrowany terminal.
Uruchom następujące
git
polecenia, aby pobrać gałąź o nazwieunit-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 nazwieorigin
.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.
W programie Visual Studio Code otwórz zintegrowany terminal.
Uruchom polecenie
dotnet build
, aby skompilować każdy projekt w rozwiązaniu.dotnet build --configuration Release
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ą,
FetchOnlyRequestedGameRegion
test jest uruchamiany pięć razy, raz dla każdej mapy gry, jak określono w danych wbudowanychTestCase
.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.
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.
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. ArgumentpublishTestResults
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
.
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.
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.
Wróć do podsumowania potoku.
Przejdź do karty Testy .
Zostanie wyświetlone podsumowanie przebiegu testu. Wszystkie pięć testów zakończyło się pomyślnie.
W usłudze Azure DevOps wybierz pozycję Plany testów, a następnie wybierz pozycję Uruchomienia.
Zobaczysz najnowsze przebiegi testów, w tym te, które właśnie uruchomiono.
Kliknij dwukrotnie najnowszy przebieg testu.
Zostanie wyświetlone podsumowanie wyników.
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 main
gałęzią , ale w celu zwięzłości pominiemy ten proces na razie.