Exercício – Corrigir um teste com falhas

Concluído

Neste ponto, tem uma forma de executar testes de unidades à medida que as alterações são processadas através do pipeline de compilação. Tem também uma forma de avaliar a quantidade de código que é abrangido pelos seus testes.

É sempre uma boa ideia executar os testes localmente antes de submeter alterações no pipeline. Mas o que acontece quando alguém se esquece e submete uma alteração que interrompe o funcionamento da compilação?

Nesta unidade, você corrigirá uma compilação quebrada causada por um teste de unidade com falha. Neste exercício, irá:

  • Obtenha o código inicial do GitHub.
  • Adicione ferramentas de cobertura de código ao seu projeto.
  • Emitir o código para o seu repositório.
  • Ver o pipeline a ser executado automaticamente e os testes de unidades a falharem.
  • Reproduzir a falha localmente.
  • Analisar e corrigir a falha.
  • Emitir uma correção e ver a compilação a ser bem-sucedida.

Rever o novo teste de unidades

A mais recente característica da equipa envolve a tabela de classificação. Precisamos obter o número de pontuações do banco de dados, para que possamos escrever um teste de unidade para verificar o IDocumentDBRepository<T>.GetItemsAsync método.

Veja a seguir o aspeto do teste. Você não precisa adicionar nenhum código ainda.

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

Lembre-se de que, num teste NUnit, TestCase fornece os dados inline que servem para testar esse método. O NUnit chama o método de teste de unidades ReturnRequestedCount da seguinte forma:

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

Este teste também utiliza a propriedade ExpectedResult para simplificar o código de teste e clarificar a sua intenção. O NUnit compara automaticamente o valor de retorno com o valor desta propriedade e elimina, assim, a necessidade de chamar explicitamente a asserção.

Escolheremos alguns valores que representam consultas típicas. Também incluiremos 0 para cobrir essa caixa de borda.

Obter o ramo do GitHub

Como você fez anteriormente, busque a failed-test ramificação no GitHub e faça check-out (ou mude para) essa ramificação.

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

  2. Execute o seguinte git fetch e comandos para baixar uma ramificação nomeada failed-test do repositório da Microsoft e git checkout alternar para essa ramificação:

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

    Nomeamos o ramo failed-test para fins de aprendizagem. Na prática, você nomearia uma filial de acordo com sua finalidade ou característica.

  3. Execute estes comandos para criar um arquivo de manifesto da ferramenta local, instalar a ReportGenerator ferramenta e adicionar o coverlet.msbuild pacote ao seu projeto de testes:

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

    Você precisa desta etapa porque a failed-test ramificação não contém o trabalho que você adicionou à unit-tests ramificação.

  4. Adicione o arquivo de projeto de teste e o arquivo de manifesto da ferramenta ao índice de preparo e confirme as alterações.

    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. Execute o seguinte git push comando para carregar a failed-test ramificação no repositório GitHub:

    git push origin failed-test
    

Ver o teste com falha no pipeline

Digamos que você estava com pressa e empurrou seu trabalho sem executar os testes uma última vez. Felizmente, o pipeline pode ajudá-lo a detetar problemas precocemente quando há testes de unidade. Você pode ver isso aqui.

  1. No Azure Pipelines, rastreie a compilação à medida que ela é executada pelo pipeline.

  2. Expanda a tarefa Executar testes de unidades – Versão à medida que é executada.

    Vai constatar que o método de teste ReturnRequestedCount falha.

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

    O teste passa quando o valor de entrada é 0, mas falha quando o valor de entrada é 1 ou 10.

    A compilação é publicada no pipeline apenas quando a tarefa anterior for bem-sucedida. Aqui, a compilação não foi publicada porque os testes de unidade falharam. Este procedimento impede que outras pessoas obtenham acidentalmente uma compilação que deixou de funcionar.

Na prática, nem sempre vai rastrear manualmente a compilação enquanto é executada. Aqui estão algumas maneiras de descobrir a falha:

  • Uma notificação por email do Azure DevOps

    Você pode configurar o Azure DevOps para enviar uma notificação por email quando a compilação for concluída. O assunto da mensagem começa por “[Falha na compilação]” quando a compilação falhar.

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

  • Planos de teste do Azure

    No Azure DevOps, selecione Planos de Teste e, em seguida, selecione Execuções. Vê as execuções de testes recentes, incluindo a que acabou de ser executada. Selecione o teste concluído mais recente. Você vê que dois dos oito testes falharam.

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

  • O painel

    No Azure DevOps, selecione Visão geral e, em seguida, selecione Painéis. Verá a falha a aparecer no widget Tendência dos Resultados dos Testes. O widget Cobertura de código está em branco, o que indica que a cobertura de código não foi executada.

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

  • O selo de construção

    Embora a ramificação não inclua o selo de compilação no arquivo README.md, aqui está o que você veria no GitHub quando a failed-test compilação falhar:

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

Analisar a falha do teste

Quando os testes de unidade falham, normalmente você tem duas opções, dependendo da natureza da falha:

  • Se o teste revelar um defeito no código, corrija o código e execute novamente os testes.
  • Se a funcionalidade mudar, ajuste o teste para corresponder aos novos requisitos.

Reproduzir a falha localmente

Nesta seção, você reproduzirá a falha localmente.

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

  2. No terminal, execute este dotnet build comando para construir o aplicativo:

    dotnet build --configuration Release
    
  3. No terminal, execute este dotnet test comando para executar os testes de unidade:

    dotnet test --no-build --configuration Release
    

    Você deve ver os mesmos erros que viu no pipeline. Aqui está parte da saída:

    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
    

Descobrir a causa do erro

Você percebe que cada teste reprovado produz um resultado que está desligado por um. Por exemplo, quando se espera o valor 10, o teste devolve um 9.

Dê uma olhada no código-fonte do método que está sendo testado, LocalDocumentDBRepository<T>.GetItemsAsync. Você deve ver isto:

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

Nesse cenário, você pode verificar o GitHub para ver se o arquivo foi alterado recentemente.

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

Você suspeita que está retornando um resultado a menos e que pageSize - 1 isso deveria ser apenas pageSize. Em nosso cenário, esse é um erro que você cometeu quando enviou o trabalho sem teste, mas em um cenário do mundo real, você pode verificar com o desenvolvedor que alterou o arquivo no GitHub para determinar o motivo da alteração.

Gorjeta

O GitHub também funciona como um local de discussão e colaboração. Pode comentar um pedido Pull ou abrir um problema.

Corrigir o erro

Nesta seção, você corrigirá o erro alterando o código de volta ao seu estado original e executando os testes para verificar a correção.

  1. No Visual Studio Code, abra Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs no explorador de arquivos.

  2. Modifique o GetItemsAsync método como mostrado aqui:

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

    Esta versão é alterada de pageSize - 1 para pageSize.

  3. Guarde o ficheiro.

  4. No terminal integrado, construa o aplicativo.

    dotnet build --configuration Release
    

    Você deve ver que a compilação é bem-sucedida.

    Na prática, você pode executar o aplicativo e experimentá-lo brevemente. Para fins de aprendizagem, vamos pular isso por enquanto.

  5. No terminal, execute os testes de unidade.

    dotnet test --no-build --configuration Release
    

    Verifica que os testes são bem-sucedidos.

    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. No terminal integrado, adicione cada arquivo modificado ao índice, confirme as alterações e envie a ramificação para o GitHub.

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

    Gorjeta

    O ponto (.) neste git add exemplo é um caractere curinga. Corresponde a todos os ficheiros não preparados no diretório atual e em todos os subdiretórios.

    Antes de usar esse caractere curinga, é uma boa prática executar git status antes de se comprometer para garantir que você esteja preparando os arquivos que pretende preparar.

  7. Volte ao Azure Pipelines. Veja a alteração mover-se através do pipeline. Os testes são aprovados e a compilação geral é bem-sucedida.

    Opcionalmente, para verificar os resultados do teste, você pode selecionar as guias Testes e Cobertura de código quando a compilação for concluída.

    Você também pode conferir o painel para visualizar a tendência de resultados atualizados.

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

Excelente! Você corrigiu a compilação. Em seguida, você aprenderá como limpar seu ambiente de DevOps do Azure.