演習 - Azure 関数の単体テストを行う

完了

単体テストは、アジャイル方法論の基本的な部分です。 Visual Studio では、テスト プロジェクト テンプレートが提供されています。 このテンプレートを使ってアプリケーションの単体テストを作成しますが、同じテクニックを Azure Functions のテストに適用できます。

高級腕時計オンライン Web サイトのシナリオの開発チームには、単体テストでコードの少なくとも 80% をカバーするというポリシーがあります。 あなたは、Azure Functions に同じポリシーを適用しようと考えています。

ここでは、Visual Studio で xUnit テスト フレームワークを使用して Azure Functions をテストする方法を説明します。

単体テスト プロジェクトを作成する

最初のステップとして、単体テストを含むプロジェクトを作成し、Azure 関数アプリを保持しているソリューションにそれを追加します。 次の手順を使用して、WatchInfo 関数をテストするための単体テスト プロジェクトを作成します。

  1. Visual Studio の [ソリューション エクスプローラー] ウィンドウで、WatchPortalFunction ソリューションを右クリックし、[追加] を選択して、[新しいプロジェクト] を選択します。

    ソリューションに新しいプロジェクトを追加するコマンドが表示されているソリューション エクスプローラーのスクリーンショット。

  2. [新しいプロジェクトの追加] ウィンドウで下にスクロールして、[xUnit テスト プロジェクト][C#+] アイコン テンプレートを選択したら、[次へ] を選択します。

    [Add New Project](新しいプロジェクトの追加) ウィンドウのスクリーンショット。xUnit Test Project テンプレートが選択されています。

  3. [Configure your new project](新しいプロジェクトを構成する) ウィンドウが表示されます。 [プロジェクト名] フィールドに、「WatchFunctionTests」と入力します。 [場所] フィールドの横の参照アイコンを選択し、WatchPortalFunction フォルダーを選択します。

  4. [次へ] を選択します。 [追加情報] ウィンドウが表示されます。

  5. [ターゲット フレームワーク] で、 既定値の [.NET 6.0 (長期的なサポート)] を受け入れます。

  6. [作成] を選択します

  7. プロジェクトが追加されたら、[ソリューション エクスプローラー] ウィンドウで WatchFunctionTests プロジェクトを右クリックし、[NuGet パッケージの管理] を選択します。

  8. [NuGet: WatchFunctionTests] ウィンドウで、[参照] タブを選択します。[検索] ボックスに「Microsoft.AspNetCore.Mvc」と入力します。 Microsoft.AspNetCore.Mvc パッケージを選択し、[インストール] を選択します。

    [NuGet パッケージの管理] ウィンドウのスクリーンショット。ユーザーは Microsoft.AspNetCore.Mvc パッケージをインストールしている。

    Note

    テスト プロジェクトで、モック HTTP 環境が作成されます。 これを行うために必要なクラスは、Microsoft.AspNetCore.Mvc パッケージに含まれます。

  9. パッケージがインストールされるまで待ちます。 [変更のプレビュー] メッセージ ボックスが表示される場合は、[OK] を選択します。 [ライセンスへの同意] メッセージ ボックスで、[同意する] を選択します。

  10. パッケージが追加されたら、[ソリューション エクスプローラー] ウィンドウの WatchFunctionsTests プロジェクトで、UnitTest1.cs ファイルを右クリックして、[名前の変更] を選択します。 ファイルの名前を WatchFunctionUnitTests.cs に変更します。 表示されるメッセージ ボックスで、UnitTest1 のすべての参照の名前を WatchFunctionUnitTests に変更するために、[はい] を選択します。

  11. [ソリューション エクスプローラー] ウィンドウの WatchFunctionsTests プロジェクトで、[依存関係] を右クリックして [プロジェクト参照の追加] を選択します。

  12. [参照マネージャー] ウィンドウで WatchPortalFunction プロジェクトを選択して、[OK] を選択します。

WatchInfo 関数の単体テストを追加する

テスト プロジェクトに単体テストを追加できます。 高級腕時計のシナリオの WatchInfo 関数では、要求のクエリ文字列でモデルが指定されている場合は OK 応答を常に返し、クエリ文字列が空か model パラメーターを含まない場合は Bad 応答を返すことを確認する必要があります。

この動作を検証するために、WatchFunctionsTests に一対の "ファクト" テストを追加します。

  1. [ソリューション エクスプローラー] ウィンドウで、コード ウィンドウに WatchPortalFunction を表示するために、WatchFunctionUnitTests.cs ファイルをダブルクリックします。

  2. ファイルの先頭で、次の using ディレクティブを一覧に追加します。

    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Http.Internal;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Primitives;
    using Microsoft.Extensions.Logging.Abstractions;
    
  3. Test1 メソッドの名前を TestWatchFunctionSuccess に変更します。

  4. 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 }
            }
        )
    };
    
  5. 次のステートメントをメソッドに追加します。 このステートメントでは、ダミーのロガーが作成されます。

    var logger = NullLoggerFactory.Instance.CreateLogger("Null Logger");
    
  6. メソッドに次のコードを追加します。 これらのステートメントでは、WatchInfo 関数が呼び出され、ダミーの要求とロガーがパラメーターとして渡されます。

    var response = WatchPortalFunction.WatchInfo.Run(request, logger);
    response.Wait();
    
  7. メソッドに次のコードを追加します。 このコードでは、関数からの応答が正しいことをチェックします。 この場合、想定される本文のデータが含まれるので、関数では 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);
    }
    
  8. 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);
    }
    

テストを実行する

  1. 上部のメニュー バーの [テスト] で、[すべてのテストを実行する] を選択します。

    Visual Studio の [テスト] メニューのスクリーンショット。ユーザーが [実行] -> [すべてのテスト] を選択しています。

  2. [テスト エクスプローラー] ウィンドウで、3 つのテストがすべて正常に完了するはずです。

    [チーム エクスプローラー] ウィンドウのスクリーンショット。3 つのテストがすべて正常に実行された。

  3. コード エディターでファイルを表示するために、[ソリューション エクスプローラー] ウィンドウで WatchPortalFunction プロジェクトの下にある WatchInfo.cs をダブルクリックします。

  4. 次のコードを見つけます。

    // Retrieve the model id from the query string
    string model = req.Query["model"];
    
  5. model 変数を設定するステートメントを次のように変更します。 この変更は、開発者がコードで間違いを犯したことをシミュレートするものです。

    string model = req.Query["modelll"];
    
  6. 上部のメニュー バーの [テスト] で、[すべてのテストを実行する] を選択します。 今回は、TestWatchFunctionSuccess のテストが失敗するはずです。 この失敗は、WatchInfo 関数がクエリ文字列で modelll という名前のパラメーターを見つけられず、Bad 応答を返したために発生します。

    [チーム エクスプローラー] ウィンドウのスクリーンショット。TestWatchFunctionSuccess のテストが失敗した。

このユニットでは、単体テスト プロジェクトを作成し、Azure 関数に単体テストを実装する方法を説明しました。