演習 - Azure 関数の単体テストを行う
単体テストは、アジャイル方法論の基本的な部分です。 Visual Studio では、テスト プロジェクト テンプレートが提供されています。 このテンプレートを使ってアプリケーションの単体テストを作成しますが、同じテクニックを Azure Functions のテストに適用できます。
高級腕時計オンライン Web サイトのシナリオの開発チームには、単体テストでコードの少なくとも 80% をカバーするというポリシーがあります。 あなたは、Azure Functions に同じポリシーを適用しようと考えています。
ここでは、Visual Studio で xUnit
テスト フレームワークを使用して Azure Functions をテストする方法を説明します。
単体テスト プロジェクトを作成する
最初のステップとして、単体テストを含むプロジェクトを作成し、Azure 関数アプリを保持しているソリューションにそれを追加します。 次の手順を使用して、WatchInfo 関数をテストするための単体テスト プロジェクトを作成します。
Visual Studio の [ソリューション エクスプローラー] ウィンドウで、WatchPortalFunction ソリューションを右クリックし、[追加] を選択して、[新しいプロジェクト] を選択します。
[新しいプロジェクトの追加] ウィンドウで下にスクロールして、[xUnit テストプロジェクト] ([C#+] アイコン) テンプレートを選択し、[次へ] を選択します。
[Configure your new project](新しいプロジェクトを構成する) ウィンドウが表示されます。 [プロジェクト名] フィールドに、「WatchFunctionTests」と入力します。 [場所] フィールドの横の参照アイコンを選択し、WatchPortalFunction フォルダーを選択します。
[次へ] を選択します。 [追加情報] ウィンドウが表示されます。
[ターゲット フレームワーク] で、 既定値の [.NET 6.0 (長期的なサポート)] を受け入れます。
[作成] を選択します
プロジェクトが追加されたら、[ソリューション エクスプローラー] ウィンドウで WatchFunctionTests プロジェクトを右クリックし、[NuGet パッケージの管理] を選択します。
[NuGet: WatchFunctionTests] ウィンドウで、[参照] タブを選択します。[検索] ボックスに「Microsoft.AspNetCore.Mvc」と入力します。 Microsoft.AspNetCore.Mvc パッケージを選択し、[インストール] を選択します。
Note
テスト プロジェクトで、モック HTTP 環境が作成されます。 これを行うために必要なクラスは、Microsoft.AspNetCore.Mvc パッケージに含まれます。
パッケージがインストールされるまで待ちます。 [変更のプレビュー] メッセージ ボックスが表示される場合は、[OK] を選択します。 [ライセンスへの同意] メッセージ ボックスで、[同意する] を選択します。
パッケージが追加されたら、[ソリューション エクスプローラー] ウィンドウの WatchFunctionsTests プロジェクトで、UnitTest1.cs ファイルを右クリックして、[名前の変更] を選択します。 ファイルの名前を WatchFunctionUnitTests.cs に変更します。 表示されるメッセージ ボックスで、UnitTest1 のすべての参照の名前を WatchFunctionUnitTests に変更するために、[はい] を選択します。
[ソリューション エクスプローラー] ウィンドウの WatchFunctionsTests プロジェクトで、[依存関係] を右クリックして [プロジェクト参照の追加] を選択します。
[参照マネージャー] ウィンドウで WatchPortalFunction プロジェクトを選択して、[OK] を選択します。
WatchInfo 関数の単体テストを追加する
テスト プロジェクトに単体テストを追加できます。 高級腕時計のシナリオの WatchInfo 関数では、要求のクエリ文字列でモデルが指定されている場合は OK 応答を常に返し、クエリ文字列が空か model
パラメーターを含まない場合は Bad 応答を返すことを確認する必要があります。
この動作を検証するために、WatchFunctionsTests に一対の "ファクト" テストを追加します。
[ソリューション エクスプローラー] ウィンドウで、コード ウィンドウに WatchPortalFunction を表示するために、WatchFunctionUnitTests.cs ファイルをダブルクリックします。
ファイルの先頭で、次の
using
ディレクティブを一覧に追加します。using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Internal; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Logging.Abstractions;
Test1 メソッドの名前を TestWatchFunctionSuccess に変更します。
TestWatchFunctionSuccess メソッドの本体に、次のコードを追加します。 このステートメントでは、モックの HTTP コンテキストと HTTP 要求が作成されます。 要求には、
abc
に設定されたmodel
パラメーターを含むクエリ文字列が含まれています。var queryStringValue = "abc"; var request = new DefaultHttpRequest(new DefaultHttpContext()) { Query = new QueryCollection ( new System.Collections.Generic.Dictionary<string, StringValues>() { { "model", queryStringValue } } ) };
次のステートメントをメソッドに追加します。 このステートメントでは、ダミーのロガーが作成されます。
var logger = NullLoggerFactory.Instance.CreateLogger("Null Logger");
メソッドに次のコードを追加します。 これらのステートメントでは、WatchInfo 関数が呼び出され、ダミーの要求とロガーがパラメーターとして渡されます。
var response = WatchPortalFunction.WatchInfo.Run(request, logger); response.Wait();
メソッドに次のコードを追加します。 このコードでは、関数からの応答が正しいことをチェックします。 この場合、想定される本文のデータが含まれるので、関数では OK 応答が返される必要があります。
// Check that the response is an "OK" response Assert.IsAssignableFrom<OkObjectResult>(response.Result); // Check that the contents of the response are the expected contents var result = (OkObjectResult)response.Result; dynamic watchinfo = new { Manufacturer = "abc", CaseType = "Solid", Bezel = "Titanium", Dial = "Roman", CaseFinish = "Silver", Jewels = 15 }; string watchInfo = $"Watch Details: {watchinfo.Manufacturer}, {watchinfo.CaseType}, {watchinfo.Bezel}, {watchinfo.Dial}, {watchinfo.CaseFinish}, {watchinfo.Jewels}"; Assert.Equal(watchInfo, result.Value);
完全なメソッドは次のようになります。
[Fact] public void TestWatchFunctionSuccess() { var queryStringValue = "abc"; var request = new DefaultHttpRequest(new DefaultHttpContext()) { Query = new QueryCollection ( new System.Collections.Generic.Dictionary<string, StringValues>() { { "model", queryStringValue } } ) }; var logger = NullLoggerFactory.Instance.CreateLogger("Null Logger"); var response = WatchPortalFunction.WatchInfo.Run(request, logger); response.Wait(); // Check that the response is an "OK" response Assert.IsAssignableFrom<OkObjectResult>(response.Result); // Check that the contents of the response are the expected contents var result = (OkObjectResult)response.Result; dynamic watchinfo = new { Manufacturer = "abc", CaseType = "Solid", Bezel = "Titanium", Dial = "Roman", CaseFinish = "Silver", Jewels = 15 }; string watchInfo = $"Watch Details: {watchinfo.Manufacturer}, {watchinfo.CaseType}, {watchinfo.Bezel}, {watchinfo.Dial}, {watchinfo.CaseFinish}, {watchinfo.Jewels}"; Assert.Equal(watchInfo, result.Value); }
TestWatchFunctionFailureNoQueryString および TestWatchFunctionFailureNoModel という名前の 2 つのメソッドをさらに追加します。 TestWatchFunctionFailureNoQueryString では、クエリ文字列が指定されていない場合に WatchInfo 関数が正常に失敗することを確認します。 TestWatchFunctionFailureNoModel では、model パラメーターが含まれないクエリ文字列が関数に渡された場合に、同じように失敗することを確認します。
[Fact] public void TestWatchFunctionFailureNoQueryString() { var request = new DefaultHttpRequest(new DefaultHttpContext()); var logger = NullLoggerFactory.Instance.CreateLogger("Null Logger"); var response = WatchPortalFunction.WatchInfo.Run(request, logger); response.Wait(); // Check that the response is an "Bad" response Assert.IsAssignableFrom<BadRequestObjectResult>(response.Result); // Check that the contents of the response are the expected contents var result = (BadRequestObjectResult)response.Result; Assert.Equal("Please provide a watch model in the query string", result.Value); } [Fact] public void TestWatchFunctionFailureNoModel() { var queryStringValue = "abc"; var request = new DefaultHttpRequest(new DefaultHttpContext()) { Query = new QueryCollection ( new System.Collections.Generic.Dictionary<string, StringValues>() { { "not-model", queryStringValue } } ) }; var logger = NullLoggerFactory.Instance.CreateLogger("Null Logger"); var response = WatchPortalFunction.WatchInfo.Run(request, logger); response.Wait(); // Check that the response is an "Bad" response Assert.IsAssignableFrom<BadRequestObjectResult>(response.Result); // Check that the contents of the response are the expected contents var result = (BadRequestObjectResult)response.Result; Assert.Equal("Please provide a watch model in the query string", result.Value); }
テストを実行する
上部のメニュー バーの [テスト] で、[すべてのテストを実行する] を選択します。
[テスト エクスプローラー] ウィンドウで、3 つのテストがすべて正常に完了するはずです。
コード エディターでファイルを表示するために、[ソリューション エクスプローラー] ウィンドウで WatchPortalFunction プロジェクトの下にある WatchInfo.cs をダブルクリックします。
次のコードを見つけます。
// Retrieve the model id from the query string string model = req.Query["model"];
model
変数を設定するステートメントを次のように変更します。 この変更は、開発者がコードで間違いを犯したことをシミュレートするものです。string model = req.Query["modelll"];
上部のメニュー バーの [テスト] で、[すべてのテストを実行する] を選択します。 今回は、TestWatchFunctionSuccess のテストが失敗するはずです。 この失敗は、WatchInfo 関数がクエリ文字列で
modelll
という名前のパラメーターを見つけられず、Bad 応答を返したために発生します。
このユニットでは、単体テスト プロジェクトを作成し、Azure 関数に単体テストを実装する方法を説明しました。