演習 - 失敗したテストを修正する
この時点で、変更がビルド パイプライン間を移動する際に、単体テストを実行する方法があります。 また、テストでカバーされるコードの量を測定する方法があります。
パイプラインに変更を送信する前に、常にローカルでテストを実行することをおすすめします。 しかし、誰かが忘れて、ビルドを中断するような変更を送信した場合はどうなるでしょう。
このユニットでは、単体テストの失敗が原因で失敗したビルドを修正します。 ここでは、次のことを行います。
- GitHub からスタート コードを取得します。
- プロジェクトにコード カバレッジ ツールを追加します。
- コードをリポジトリにプッシュする。
- パイプラインの自動実行と単体テストの失敗を監視する。
- ローカルで失敗を再現する。
- 失敗を分析して修正する。
- 修正をプッシュし、ビルドの成功を監視する。
新しい単体テストを確認する
チームの最新機能にはランキングが含まれています。 ここではデータベースからスコアの数を取得する必要があるため、IDocumentDBRepository<T>.GetItemsAsync
メソッドを検証する単体テストを記述することができます。
テストは次のように表示されます。 今はまだ、コードを追加する必要はありません。
[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();
}
NUnit テストでは、TestCase
で、そのメソッドをテストするために使用するインライン データが提供されることを思い出してください。 NUnit では、このような ReturnRequestedCount
単体テスト メソッドを呼び出します。
ReturnRequestedCount(0);
ReturnRequestedCount(1);
ReturnRequestedCount(10);
また、このテストの場合、ExpectedResult
プロパティを使用してテスト コードを簡略化し、その意図を明確にするのに役立ちます。 NUnit では、戻り値とこのプロパティの値が自動的に比較され、アサーションを明示的に呼び出す必要がなくなります。
一般的なクエリを表すいくつかの値を選択します。 エッジ ケースをカバーするために 0 も含めます。
GitHub からブランチをフェッチする
前に行ったように、GitHub から failed-test
ブランチをフェッチして、そのブランチをチェックアウトします (またはそのブランチに切り替えます)。
Visual Studio Code で、統合ターミナルを開きます。
次の
git fetch
およびgit checkout
コマンドを実行して、Microsoft のリポジトリからfailed-test
という名前のブランチをダウンロードし、そのブランチに切り替えます。git fetch upstream failed-test git checkout -B failed-test upstream/failed-test
学習目的のため、ブランチには
failed-test
という名前を付けました。 実際には、目的や機能に基づいてブランチに名前を付けます。次のコマンドを実行して、ローカルのツール マニフェスト ファイルを作成し、
ReportGenerator
ツールをインストールして、テスト プロジェクトにcoverlet.msbuild
パッケージを追加します。dotnet new tool-manifest dotnet tool install dotnet-reportgenerator-globaltool dotnet add Tailspin.SpaceGame.Web.Tests package coverlet.msbuild
failed-test
ブランチにはunit-tests
ブランチに追加した作業が含まれていないため、この手順が必要になります。テスト プロジェクト ファイルとツール マニフェスト ファイルをステージング インデックスに追加し、変更をコミットします。
git add Tailspin.SpaceGame.Web.Tests/Tailspin.SpaceGame.Web.Tests.csproj git add .config/dotnet-tools.json git commit -m "Configure code coverage tests"
次の
git push
コマンドを実行し、GitHub リポジトリにfailed-test
ブランチをアップロードします。git push origin failed-test
パイプラインでテストの失敗を確認する
たとえば、あなたは急いでいて最後にもう一度テストを実行せずに作業内容をプッシュしたとします。 幸い、単体テストがある場合、早い段階で問題を見つけるためにパイプラインが役立つ場合があります。 それをここで確認できます。
Azure Pipelines で、パイプラインを介してビルドが実行されているときにビルドをトレースします。
実行時に Run unit tests - Release タスクを展開します。
ReturnRequestedCount
テスト メソッドが失敗していることがわかります。入力値が 0 の場合はテストが成功しますが、入力値が 1 または 10 の場合は失敗します。
ビルドは、前のタスクが成功した場合にのみ、パイプラインに発行されます。 ここでは、単体テストが失敗したため、ビルドは発行されませんでした。 これで、他のユーザーが中断されたビルドを誤って取得しなくなります。
実際には、必ずしも実行時にビルドを手動でトレースするとは限りません。 失敗を検出できる方法をいくつか以下に示します。
Azure DevOps からの電子メール通知
ビルドの完了時に電子メール通知を送信するように Azure DevOps を構成できます。 ビルドに失敗した場合、件名は "[ビルドに失敗しました]" で始まります。
Azure Test Plans
Azure DevOps で、[Test Plans] を選択してから、[Runs] を選択します。 先ほど実行されたものを含め、最近のテストの実行が示されます。 完了した最新のテストを選択します。 8 つのテストのうち、2 つが失敗したことがわかります。
ダッシュボード
Azure DevOps で、[Overview] を選択してから、[Dashboards] を選択します。 [テスト結果の傾向] ウィジェットに失敗が示されているのがわかります。 [Code Coverage] ウィジェットが空白になっており、コード カバレッジが実行されなかったことが示されています。
ビルド バッジ
failed-test
ブランチでは README.md ファイルにビルド バッジは含まれていませんが、ビルドに失敗した場合、GitHub には次のように表示されます。
テストの失敗を分析する
単体テストが失敗したときに、通常は失敗の性質に応じて、2 つの選択肢があります。
- テストでコードの不備が明らかになった場合、コードを修正し、テストを再実行します。
- 機能を変更した場合は、新しい要件が満たされるようにテストを調整します。
ローカルで失敗を再現する
このセクションでは、エラーをローカルで再現します。
Visual Studio Code で、統合ターミナルを開きます。
ターミナルで、この
dotnet build
コマンドを実行して、アプリケーションをビルドします。dotnet build --configuration Release
ターミナルで、この
dotnet test
コマンドを実行して、単体テストを実行します。dotnet test --no-build --configuration Release
パイプラインで確認したのと同じエラーが確認できるはずです。 出力の一部を以下に示します。
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
エラーの原因を見つける
あなたは失敗した各テストが 1 つ少ない結果を生成していることに気付きます。 たとえば、10 である必要がある場合、テストでは 9 が返されています。
テスト中のメソッド LocalDocumentDBRepository<T>.GetItemsAsync
のソース コードを確認します。 次のように表示されます。
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);
}
このシナリオでは、GitHub をチェックして、ファイルが最近変更されたかどうかを確認できます。
あなたは、pageSize - 1
が 1 つ少ない結果を返しており、これは pageSize
であるべきではないかと考えます。 このシナリオでは、これはテストなしで作業内容をプッシュしたときに発生したエラーですが、実際のシナリオでは、GitHub 上でファイルを変更した開発者とチェックを行い、変更の理由を特定できます。
ヒント
ディスカッションや共同作業が GitHub で行われる場合もあります。 pull request でコメントしたり、問題を開いたりすることができます。
エラーを修正する
このセクションでは、コードを元の状態に戻し、修正を確認するためのテストを実行することで、エラーを修正します。
Visual Studio Code で、エクスプローラーから Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs を開きます。
GetItemsAsync
メソッドを次に示すように変更します。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); }
このバージョンでは
pageSize - 1
をpageSize
に変更します。ファイルを保存します。
統合ターミナルで、アプリケーションをビルドします。
dotnet build --configuration Release
ビルドの成功を確認できるはずです。
実際は、アプリを実行し、それを簡単に試す場合があります。学習目的のため、今のところ、それはスキップします。
ターミナルで、単体テストを実行します。
dotnet test --no-build --configuration Release
テストに成功したことがわかります。
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
統合ターミナルで、変更された各ファイルをインデックスに追加し、変更をコミットして、GitHub にブランチをプッシュします。
git add . git commit -m "Return correct number of items" git push origin failed-test
ヒント
この
git add
の例のドット (.
) はワイルドカード文字です。 これは、現在のディレクトリおよびすべてのサブディレクトリ内のステージングされていないすべてのファイルと一致します。このワイルドカード文字を使用するにあたっては、コミットの前に
git status
を実行して、ステージングを行いたいファイルをステージングしていることを確認することをお勧めします。Azure Pipelines に戻ります。 パイプラインでの変更の移動を監視します。 テストが成功し、ビルド全体が成功します。
必要に応じて、テスト結果を確認するには、ビルドの完了時に [Tests] タブと [Code Coverage] タブを選択します。
また、ダッシュボードを調べて、更新された結果の傾向を表示することもできます。
すばらしい。 ビルドの修正が完了しました。 次は、Azure DevOps 環境をクリーンアップする方法について説明します。