Esercizio - Correggere un test non superato
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.
Aprire il terminale integrato in Visual Studio Code.
Eseguire i comandi
git fetch
egit checkout
seguenti per scaricare un ramo denominatofailed-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.Eseguire questi comandi per creare un file manifesto dello strumento locale, installare lo strumento
ReportGenerator
e aggiungere ai progetti di test il pacchettocoverlet.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 ramounit-tests
.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"
Eseguire questo comando
git push
per caricare il ramofailed-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.
In Azure Pipelines tenere traccia della compilazione mentre viene eseguita attraverso la pipeline.
Espandere l'attività Esegui unit test - Rilascio durante l'esecuzione.
Il metodo di test
ReturnRequestedCount
non riesce.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.
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.
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.
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:
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.
Aprire il terminale integrato in Visual Studio Code.
Nel terminale eseguire il comando
dotnet build
per compilare l'applicazione:dotnet build --configuration Release
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.
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.
In Esplora file di Visual Studio Code aprire Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs.
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
inpageSize
.Salva il file.
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.
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
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 digit 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.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.
Ottimo. La compilazione è stata corretta. Si apprenderà ora come pulire l'ambiente di Azure DevOps.