Esercizio - Correggere un test non superato

Completato

A questo punto, è possibile eseguire unit test mentre le modifiche si spostano attraverso la pipeline di compilazione. È anche possibile misurare la quantità di code coverage dei test.

È sempre consigliabile eseguire i test in locale prima di inviare le modifiche alla pipeline. Ma cosa succede se qualcuno se ne dimentica e invia una modifica che interrompe la compilazione?

In questa unità si correggerà l'interruzione di una build causata da uno unit test non superato. Deve eseguire queste operazioni:

  • Ottenere codice di avvio da GitHub.
  • Aggiungere al progetto gli strumenti di code coverage.
  • Eseguire il push del codice nel proprio repository.
  • Osservare l'esecuzione automatica della pipeline e l'esito negativo degli unit test.
  • Riprodurre l'errore in locale.
  • Analizzare e correggere l'errore.
  • Eseguire il push di una correzione e verificare il corretto funzionamento della compilazione.

Esaminare il nuovo unit test

La funzionalità più recente del team include la classifica. È necessario ottenere il numero di punteggi dal database, in modo che sia possibile scrivere uno unit test per verificare il metodo IDocumentDBRepository<T>.GetItemsAsync.

Ecco come risulta il test. Non è ancora necessario aggiungere codice.

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

Nei test NUnit TestCase fornisce i dati inline da usare per testare questo metodo. NUnit chiama il metodo di unit test ReturnRequestedCount in questo modo:

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

Questo test usa anche la proprietà ExpectedResult per semplificare il codice del test e chiarirne l'intento. NUnit confronta automaticamente il valore restituito con il valore di questa proprietà, eliminando la necessità di chiamare esplicitamente l'asserzione.

Verranno scelti alcuni valori che rappresentano le query tipiche. Si includerà anche 0 per tenere conto di questo caso limite.

Recuperare il ramo da GitHub

Come in precedenza, recuperare ed estrarre il ramo failed-test da GitHub oppure passare a tale ramo.

  1. Aprire il terminale integrato in Visual Studio Code.

  2. Eseguire i comandi git fetch e git checkout seguenti per scaricare un ramo denominato failed-test dal repository di Microsoft e passare a tale ramo:

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

    A fini dell'apprendimento il ramo è denominato failed-test. Nella pratica, il nome del ramo verrà scelto in base allo scopo o alla funzionalità che svolge.

  3. Eseguire questi comandi per creare un file manifesto dello strumento locale, installare lo strumento ReportGenerator e aggiungere ai progetti di test il pacchetto coverlet.msbuild:

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

    Questo passaggio è necessario perché il ramo failed-test non contiene il lavoro aggiunto al ramo unit-tests.

  4. Aggiungere all'indice di gestione temporanea il file del progetto di test e il file manifesto dello strumento ed eseguire il commit delle modifiche.

    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. Eseguire questo comando git push per caricare il ramo failed-test nel proprio repository GitHub:

    git push origin failed-test
    

Osservare il test non superato nella pipeline

Si supponga che nella fretta il lavoro sia stato completato senza eseguire un'ultima volta i test. Fortunatamente, quando sono presenti unit test, la pipeline consente di rilevare presto i problemi. Ecco come.

  1. In Azure Pipelines tenere traccia della compilazione mentre viene eseguita attraverso la pipeline.

  2. Espandere l'attività Esegui unit test - Rilascio durante l'esecuzione.

    Il metodo di test ReturnRequestedCount non riesce.

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

    Il test viene superato quando il valore di input è 0, ma ha esito negativo quando il valore di input è 1 o 10.

    La compilazione viene pubblicata nella pipeline solo quando le attività precedenti riescono. In questo caso la build non è stata pubblicata a causa dell'esito negativo degli unit test. Di conseguenza, altre persone non otterranno accidentalmente una compilazione interrotta.

Nella pratica la compilazione non verrà sempre tracciata manualmente durante l'esecuzione. Ecco come sarebbe possibile individuare l'errore:

  • Una notifica tramite posta elettronica inviata da Azure DevOps

    È possibile configurare Azure DevOps in modo che invii una notifica tramite posta elettronica al completamento della compilazione. La riga dell'oggetto inizia con "[Compilazione non riuscita]" nel caso la compilazione non riesca.

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

  • Azure Test Plans

    In Azure DevOps selezionare Test Plans, quindi Esecuzioni. Vengono visualizzate le esecuzioni dei test recenti, incluso quello appena eseguito. Selezionare l'ultimo test completato. Come si può vedere, due degli otto test non sono stati superati.

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

  • Il dashboard

    In Azure DevOps selezionare Panoramica e quindi Dashboard. Nel widget Tendenza risultati del test viene visualizzato l'errore. Il widget Code coverage è vuoto, a indicare che il code coverage non è stato eseguito.

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

  • La notifica sulla compilazione

    Anche se il ramo failed-test non include la notifica sulla compilazione nel file README.md, quando la compilazione ha esito negativo in GitHub viene visualizzato quanto segue:

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

Analizzare l'errore dei test

Quando gli unit test hanno esito negativo, sono in genere disponibili due opzioni, a seconda della natura dell'errore:

  • Se il test rivela un difetto nel codice, correggere il codice ed eseguire nuovamente i test.
  • Se la funzionalità cambia, modificare il test in base ai nuovi requisiti.

Riprodurre l'errore in locale

In questa sezione si riprodurrà l'errore in locale.

  1. Aprire il terminale integrato in Visual Studio Code.

  2. Nel terminale eseguire il comando dotnet build per compilare l'applicazione:

    dotnet build --configuration Release
    
  3. Nel terminale eseguire il comando dotnet test per eseguire gli unit test:

    dotnet test --no-build --configuration Release
    

    Dovrebbero essere visualizzati gli stessi errori mostrati nella pipeline. Ecco parte dell'output restituito:

    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
    

Trovare la causa dell'errore

Come si può notare, ogni test non superato produce un risultato con uno scarto di uno. Ad esempio, se è previsto 10, il test restituisce 9.

Esaminare il codice sorgente del metodo sottoposto a test, LocalDocumentDBRepository<T>.GetItemsAsync. L'immagine visualizzata è simile alla seguente:

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

In questo scenario, è possibile controllare GitHub per verificare se il file è stato modificato di recente.

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

Si sospetta che pageSize - 1 restituisca un risultato in meno, che dovrebbe essere pageSize. In questo scenario si tratta di un errore che si è verificato quando è stato eseguito il push senza test, ma in uno scenario reale è possibile rivolgersi allo sviluppatore che ha modificato il file in GitHub per determinare il motivo della modifica.

Suggerimento

Anche GitHub offre la possibilità di discutere e collaborare. È possibile aggiungere un commento su una richiesta pull oppure aprire un problema.

Correggere l'errore

In questa sezione verrà corretto l'errore ripristinando lo stato originale del codice ed eseguendo i test per verificare la correzione.

  1. In Esplora file di Visual Studio Code aprire Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs.

  2. Modificare il metodo GetItemsAsync come illustrato di seguito:

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

    Questa versione cambia pageSize - 1 in pageSize.

  3. Salva il file.

  4. Nel terminale integrato compilare l'applicazione.

    dotnet build --configuration Release
    

    La compilazione dovrebbe avere esito positivo.

    Nella pratica, si potrebbe eseguire l'app e provarla brevemente. Ai fini dell'apprendimento, questo passaggio verrà ignorato per il momento.

  5. Nel terminale eseguire gli unit test.

    dotnet test --no-build --configuration Release
    

    I test vengono superati.

    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. Nel terminale integrato aggiungere all'indice ogni file modificato, eseguire il commit delle modifiche ed eseguire il push del ramo in GitHub.

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

    Suggerimento

    Il punto (.) in questo esempio di git add è un carattere jolly. Trova tutti i file non preparati per il commit nella directory corrente e in tutte le sottodirectory.

    Prima di usare questo carattere jolly, è consigliabile eseguire git status prima del commit per assicurarsi che i file che vengono preparati per il commit siano quelli previsti.

  7. Tornare in Azure Pipelines. Osservare la modifica che si sposta attraverso la pipeline. I test vengono superati e la build complessiva ha esito positivo.

    Facoltativamente, per verificare i risultati del test, al completamento della build è possibile selezionare le schede Test e Code coverage.

    È anche possibile controllare il dashboard per vedere la tendenza aggiornata dei risultati.

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

Ottimo. La compilazione è stata corretta. Si apprenderà ora come pulire l'ambiente di Azure DevOps.