Exercício – Adicionar testes de unidades à aplicação

Concluído

Nesta unidade, adicionaremos testes de unidade à compilação automatizada que criamos com o Microsoft Azure Pipelines. Os bugs de regressão estão se infiltrando no código da sua equipe e quebrando a funcionalidade de filtragem da tabela de classificação. Especificamente, o modo de jogo errado está sempre a aparecer.

A imagem a seguir ilustra o problema. Quando um usuário seleciona "Via Láctea" para mostrar apenas pontuações desse mapa de jogo, ele obtém resultados de outros mapas de jogo, como Andrômeda.

A screenshot of the leaderboard showing incorrect results: Andromeda galaxy scores show in the Milky Way galaxy listing.

A equipe quer pegar o erro antes que ele chegue aos testadores. Os testes de unidades são uma excelente forma de testar automaticamente os erros de regressão.

Adicionar os testes de unidade neste ponto do processo dará à equipe uma vantagem inicial à medida que eles melhoram o aplicativo Web Space Game . A aplicação utiliza uma base de dados de documentos para armazenar as pontuações máximas e os perfis dos jogadores. Neste momento, utiliza dados de teste locais. Mais tarde, planeiam ligar a aplicação a uma base de dados em direto.

Muitas estruturas de teste de unidade estão disponíveis para aplicativos C#. Vamos usar o NUnit porque é popular entre a comunidade.

Aqui está o teste de unidade com o qual você está trabalhando:

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

Pode filtrar as classificações por qualquer combinação de tipo de jogo e mapa de jogo.

Este teste consulta as classificações das pontuações máximas e verifica se cada resultado corresponde ao mapa de jogo fornecido.

Num método de teste NUnit, TestCase fornece dados inline que servem para testar esse método. Aqui, o NUnit chama o método de teste de unidade, da FetchOnlyRequestedGameRegion seguinte maneira:

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

Repare na chamada para o método Assert.That no final do teste. Uma asserção é uma condição ou declaração que declara como verdadeira. Se a condição se revelar falsa, tal poderá indicar um erro no código. O NUnit executa cada método de teste através dos dados inline que especificar e regista o resultado como um teste aprovado ou com falhas.

Muitas arquiteturas de teste de unidades fornecem métodos de verificação que se assemelham a linguagem natural. Esses métodos ajudam a tornar os testes fáceis de ler e ajudam a mapear os testes para os requisitos do aplicativo.

Considere a afirmação feita neste exemplo:

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

Pode ler esta linha como:

Afirmar que a região de jogo de cada pontuação devolvida corresponde à região do jogo fornecida.

Aqui está o processo a seguir:

  1. Buscar uma ramificação do repositório GitHub que contém os testes de unidade.
  2. Execute os testes localmente para verificar se passam.
  3. Adicione tarefas à configuração do pipeline para executar os testes e recolher os resultados.
  4. Emita o ramo para o repositório do GitHub.
  5. Veja o projeto do Azure Pipelines a criar automaticamente a aplicação e a executar os testes.

Obter o ramo do GitHub

Aqui, você buscará a unit-tests ramificação no GitHub e fará check-out, ou alternará para essa ramificação.

Esta ramificação contém o projeto Space Game com o qual você trabalhou nos módulos anteriores e uma configuração do Azure Pipelines para começar.

  1. No Visual Studio Code, abra o terminal integrado.

  2. Execute os seguintes git comandos para buscar uma ramificação nomeada unit-tests no repositório da Microsoft e, em seguida, alterne para essa ramificação.

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

    O formato deste comando permite que você obtenha o código inicial do repositório Microsoft GitHub, conhecido como upstream. Em breve, você enviará essa ramificação para o repositório GitHub, conhecido como origin.

  3. Como etapa opcional, abra o arquivo azure-pipelines.yml no Visual Studio Code e familiarize-se com a configuração inicial. A configuração é parecida com a básica que criou no módulo Criar um pipeline de compilação com o Azure Pipelines. O módulo cria apenas a configuração da Versão da aplicação.

Executar os testes localmente

É uma boa ideia executar todos os testes localmente antes de submeter quaisquer testes ao pipeline. É o que fará aqui.

  1. No Visual Studio Code, abra o terminal integrado.

  2. Execute dotnet build para criar cada projeto na solução.

    dotnet build --configuration Release
    
  3. Execute o seguinte dotnet test comando para executar os testes de unidade:

    dotnet test --configuration Release --no-build
    

    O sinalizador --no-build especifica para não compilar o projeto antes de o executar. Não precisa de compilar o projeto, uma vez que o criou no passo anterior.

    Você deve ver que todos os cinco testes passam.

    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
    

    Neste exemplo, os testes levaram menos de um segundo para serem executados.

    Tenha em atenção que, no total, havia cinco testes. Embora tenhamos definido apenas um método de teste, esse teste é executado cinco vezes, uma vez para cada mapa de jogo, FetchOnlyRequestedGameRegionconforme especificado nos TestCase dados embutidos.

  4. Execute os testes uma segunda vez. Desta vez, forneça a opção --logger para escrever os resultados para um ficheiro de registo.

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

    Você vê na saída que um arquivo TRX é criado no diretório TestResults .

    Um ficheiro TRX é um documento XML que contém os resultados de uma execução de teste. É um formato popular para resultados de teste porque o Visual Studio e outras ferramentas podem ajudá-lo a visualizar os resultados.

    Mais tarde, você verá como o Azure Pipelines pode ajudá-lo a visualizar e acompanhar os resultados do teste à medida que eles são executados pelo pipeline.

    Nota

    Os ficheiros TRX não se destinam a ser incluídos no controlo de origem. Um arquivo .gitignore permite que você especifique quais arquivos temporários e outros você deseja que o Git ignore. O ficheiro .gitignore do projeto já está configurado para ignorar tudo no diretório TestResults.

  5. Como uma etapa opcional, no Visual Studio Code, abra o arquivo de DocumentDBRepository_GetItemsAsyncShould.cs da pasta Tailspin.SpaceGame.Web.Tests e examine o código de teste. Mesmo que você não esteja interessado em criar aplicativos .NET especificamente, você pode achar o código de teste útil porque ele se assemelha ao código que você pode ver em outras estruturas de teste de unidade.

Adicionar tarefas à configuração do pipeline

Aqui, você configurará o pipeline de compilação para executar seus testes de unidade e coletar os resultados.

  1. No Visual Studio Code, modifique azure-pipelines.yml da seguinte maneira:

    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()
    

    Esta versão apresenta esta tarefa de compilação DotNetCoreCLI@2.

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

    Esta tarefa de compilação executa o comando dotnet test.

    Observe que essa tarefa não especifica o --logger trx argumento que você usou quando executou os testes manualmente. O argumento publishTestResults adiciona isso por si. Este argumento indica ao pipeline para gerar o ficheiro TRX para um diretório temporário, acessível através da variável incorporada $(Agent.TempDirectory). Também publica os resultados da tarefa no pipeline.

    O projects argumento especifica todos os projetos C# que correspondem a "**/*. Testes.csproj." A parte "**" corresponde a todos os diretórios e o "*. Tests.csproj" parte corresponde a todos os projetos cujo nome de arquivo termina com ". Testes.csproj." A unit-tests ramificação contém apenas um projeto de teste de unidade, Tailspin.SpaceGame.Web.Tests.csproj. Especificando um padrão, você pode executar mais projetos de teste sem a necessidade de modificar sua configuração de compilação.

Emitir o ramo para o GitHub

Aqui, você enviará suas alterações para o GitHub e verá o pipeline ser executado. Lembre-se de que está atualmente no ramo unit-tests.

  1. No terminal integrado, adicione azure-pipelines.yml ao índice, confirme as alterações e envie a ramificação para o GitHub.

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

Veja o Azure Pipelines a executar os testes

Aqui você vê os testes executados no pipeline e, em seguida, visualiza os resultados dos Planos de Teste do Microsoft Azure. Os Planos de Teste do Azure fornecem todas as ferramentas de que precisa para testar com êxito as suas aplicações. Você pode criar e executar planos de teste manuais, gerar testes automatizados e coletar feedback das partes interessadas.

  1. No Azure Pipelines, rastreie a compilação através de cada uma das etapas.

    Verá que a tarefa Executar testes de unidades – Versão executa os testes de unidades, tal como fez manualmente na linha de comandos.

    A screenshot of Azure Pipelines showing console output from running unit tests.

  2. Navegue de volta para o resumo do pipeline.

  3. Aceda ao separador Testes.

    Verá um resumo da execução de testes. Os cinco testes foram aprovados.

    A screenshot of Azure Pipelines showing the Tests tab with 5 total tests run and 100 percent passing.

  4. No Azure DevOps, selecione Planos de Teste e, em seguida, selecione Execuções.

    A screenshot of Azure DevOps navigation menu with Test Plans section and Runs tab highlighted.

    Vê as execuções de testes mais recentes, incluindo a que acabou de executar.

  5. Clique duas vezes na execução de teste mais recente.

    Verá um resumo dos resultados.

    A screenshot of Azure DevOps test run results summary showing 5 passed tests.

    Neste exemplo, os cinco testes foram aprovados. Se algum teste falhar, você pode ir para a tarefa de compilação para obter mais detalhes.

    Você também pode baixar o arquivo TRX para examiná-lo através do Visual Studio ou outra ferramenta de visualização.

Embora você tenha adicionado apenas um teste, é um bom começo e corrige o problema imediato. Agora, a equipa tem um local para adicionar mais testes e executá-los à medida que melhoram o processo.

Mesclar sua filial na principal

Em um cenário do mundo real, se você ficou satisfeito com os resultados, pode mesclar a ramificação com main, mas, por uma questão de brevidade, unit-tests vamos pular esse processo por enquanto.