Azure Functions のコードをテストするための戦略Strategies for testing your code in Azure Functions

この記事では、Azure Functions の自動化テストを作成する方法を示します。This article demonstrates how to create automated tests for Azure Functions.

すべてのコードをテストすることをお勧めします。ただし、Functions のロジックをまとめて Function の外部にテストを作成することで、最適な結果が得られる場合があります。Testing all code is recommended, however you may get the best results by wrapping up a Function's logic and creating tests outside the Function. ロジックを切り離して抽象化することで、Function のコード行が制限され、Function 単独で他のクラスやモジュールを呼び出すことができます。Abstracting logic away limits a Function's lines of code and allows the Function to be solely responsible for calling other classes or modules. ただし、この記事では、HTTP およびタイマーによってトリガーされる関数に対して自動化テストを作成する方法を示します。This article, however, demonstrates how to create automated tests against an HTTP and timer-triggered function.

次のコンテンツは、異なる言語と環境を対象とするため、2 つの異なるセクションに分かれています。The content that follows is split into two different sections meant to target different languages and environments. 次の言語でのテストの作成を学習できます。You can learn to build tests in:

サンプル リポジトリは GitHub で入手できます。The sample repository is available on GitHub.

Visual Studio での C#C# in Visual Studio

次の例では、Visual Studio で C# 関数アプリを作成し、xUnit を使用して実行およびテストする方法を説明します。The following example describes how to create a C# Function app in Visual Studio and run and tests with xUnit.

Visual Studio で C# を使って Azure Functions をテストする

セットアップSetup

環境を設定するには、関数を作成し、アプリをテストします。To set up your environment, create a Function and test app. 次の手順では、テストをサポートするために必要なアプリと関数を作成できます。The following steps help you create the apps and functions required to support the tests:

  1. 新しい関数アプリを作成して Functions という名前を付けますCreate a new Functions app and name it Functions
  2. テンプレートから HTTP 関数を作成して HttpTrigger という名前を付けます。Create an HTTP function from the template and name it HttpTrigger.
  3. テンプレートからタイマー関数を作成して TimerTrigger という名前を付けます。Create a timer function from the template and name it TimerTrigger.
  4. Visual Studio で [ファイル] > [新規作成] > [プロジェクト] > [Visual C#] > [.NET Core] > [xUnit テスト プロジェクト] の順にクリックして xUnit テスト アプリを作成し、Functions.Test という名前を付けます。Create an xUnit Test app in Visual Studio by clicking File > New > Project > Visual C# > .NET Core > xUnit Test Project and name it Functions.Test.
  5. Nuget を使用して、テスト アプリ Microsoft.AspNetCore.Mvc からの参照を追加しますUse Nuget to add a references from the test app Microsoft.AspNetCore.Mvc
  6. Functions.Test アプリから Functions アプリを参照します。Reference the Functions app from Functions.Test app.

テスト クラスの作成Create test classes

これでアプリケーションが作成されたため、自動テストの実行に使用するクラスを作成できます。Now that the applications are created, you can create the classes used to run the automated tests.

各関数は、ILogger のインスタンスを取得して、メッセージ ログを処理します。Each function takes an instance of ILogger to handle message logging. テストによっては、メッセージをログに記録しないことも、ログの実装方法に対する懸念がないこともあります。Some tests either don't log messages or have no concern for how logging is implemented. その他のテストは、テストを渡すかどうかを判断するために、ログに記録されたメッセージを評価する必要があります。Other tests need to evaluate messages logged to determine whether a test is passing.

ListLogger クラスは、ILogger インターフェイスを実装するためのもので、テスト中に評価するメッセージの内部リストに保持されます。The ListLogger class is meant to implement the ILogger interface and hold in internal list of messages for evaluation during a test.

Functions.Test アプリケーションを右クリックし、 [追加] > [クラス] の順に選択し、NullScope.cs という名前を付けて、次のコードを入力します。Right-click on the Functions.Test application and select Add > Class, name it NullScope.cs and enter the following code:

using System;

namespace Functions.Tests
{
    public class NullScope : IDisposable
    {
        public static NullScope Instance { get; } = new NullScope();

        private NullScope() { }

        public void Dispose() { }
    }
}

次に、Functions.Test アプリケーションを右クリックし、 [追加] > [クラス] の順に選択し、ListLogger.cs という名前を付けて、次のコードを入力します。Next, right-click on the Functions.Test application and select Add > Class, name it ListLogger.cs and enter the following code:

using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;

namespace Functions.Tests
{
    public class ListLogger : ILogger
    {
        public IList<string> Logs;

        public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;

        public bool IsEnabled(LogLevel logLevel) => false;

        public ListLogger()
        {
            this.Logs = new List<string>();
        }

        public void Log<TState>(LogLevel logLevel, 
                                EventId eventId,
                                TState state,
                                Exception exception,
                                Func<TState, Exception, string> formatter)
        {
            string message = formatter(state, exception);
            this.Logs.Add(message);
        }
    }
}

ListLogger クラスは、ILogger インターフェイスによって縮小される次のメンバーを実装します。The ListLogger class implements the following members as contracted by the ILogger interface:

  • BeginScope:スコープがログにコンテキストを追加します。BeginScope: Scopes add context to your logging. この場合、テストで NullScope クラスの静的インスタンスをポイントするだけで、テストを機能させることができます。In this case, the test just points to the static instance on the NullScope class to allow the test to function.

  • IsEnabled:既定の値 false が指定されます。IsEnabled: A default value of false is provided.

  • Log:このメソッドは、指定された formatter 関数を使用してメッセージの書式を設定してから、結果のテキストを Logs コレクションに追加します。Log: This method uses the provided formatter function to format the message and then adds the resulting text to the Logs collection.

Logs コレクションは List<string> のインスタンスであり、コンストラクターで初期化されます。The Logs collection is an instance of List<string> and is initialized in the constructor.

次に、Functions.Test アプリケーションを右クリックし、 [追加] > [クラス] の順に選択し、LoggerTypes.cs という名前を付けて、次のコードを入力します。Next, right-click on the Functions.Test application and select Add > Class, name it LoggerTypes.cs and enter the following code:

namespace Functions.Tests
{
    public enum LoggerTypes
    {
        Null,
        List
    }
}

この列挙型は、テストで使用されるロガーの種類を指定します。This enumeration specifies the type of logger used by the tests.

次に、Functions.Test アプリケーションを右クリックし、 [追加] > [クラス] の順に選択し、TestFactory.cs という名前を付けて、次のコードを入力します。Next, right-click on the Functions.Test application and select Add > Class, name it TestFactory.cs and enter the following code:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Primitives;
using System.Collections.Generic;

namespace Functions.Tests
{
    public class TestFactory
    {
        public static IEnumerable<object[]> Data()
        {
            return new List<object[]>
            {
                new object[] { "name", "Bill" },
                new object[] { "name", "Paul" },
                new object[] { "name", "Steve" }

            };
        }

        private static Dictionary<string, StringValues> CreateDictionary(string key, string value)
        {
            var qs = new Dictionary<string, StringValues>
            {
                { key, value }
            };
            return qs;
        }

        public static DefaultHttpRequest CreateHttpRequest(string queryStringKey, string queryStringValue)
        {
            var request = new DefaultHttpRequest(new DefaultHttpContext())
            {
                Query = new QueryCollection(CreateDictionary(queryStringKey, queryStringValue))
            };
            return request;
        }

        public static ILogger CreateLogger(LoggerTypes type = LoggerTypes.Null)
        {
            ILogger logger;

            if (type == LoggerTypes.List)
            {
                logger = new ListLogger();
            }
            else
            {
                logger = NullLoggerFactory.Instance.CreateLogger("Null Logger");
            }

            return logger;
        }
    }
}

TestFactory クラスは、次のメンバーを実装します。The TestFactory class implements the following members:

  • Data:このプロパティは、サンプル データの IEnumerable コレクションを返します。Data: This property returns an IEnumerable collection of sample data. キー値のペアは、クエリ文字列に渡される値を表します。The key value pairs represent values that are passed into a query string.

  • CreateDictionary:このメソッドは、キー/値ペアを引数として受け取り、クエリ文字列値を表す QueryCollection を作成するために使用する新しい Dictionary を返します。CreateDictionary: This method accepts a key/value pair as arguments and returns a new Dictionary used to create QueryCollection to represent query string values.

  • CreateHttpRequest:このメソッドは、指定されたクエリ文字列パラメーターで初期化される HTTP 要求を作成します。CreateHttpRequest: This method creates an HTTP request initialized with the given query string parameters.

  • CreateLogger:このメソッドは、ロガーの種類に応じて、テストに使用されるロガー クラスを返します。CreateLogger: Based on the logger type, this method returns a logger class used for testing. ListLogger は、テストで評価に使用できるログ記録されたメッセージを追跡します。The ListLogger keeps track of logged messages available for evaluation in tests.

次に、Functions.Test アプリケーションを右クリックし、 [追加] > [クラス] の順に選択し、FunctionsTests.cs という名前を付けて、次のコードを入力します。Next, right-click on the Functions.Test application and select Add > Class, name it FunctionsTests.cs and enter the following code:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Xunit;

namespace Functions.Tests
{
    public class FunctionsTests
    {
        private readonly ILogger logger = TestFactory.CreateLogger();

        [Fact]
        public async void Http_trigger_should_return_known_string()
        {
            var request = TestFactory.CreateHttpRequest("name", "Bill");
            var response = (OkObjectResult)await HttpTrigger.Run(request, logger);
            Assert.Equal("Hello, Bill", response.Value);
        }

        [Theory]
        [MemberData(nameof(TestFactory.Data), MemberType = typeof(TestFactory))]
        public async void Http_trigger_should_return_known_string_from_member_data(string queryStringKey, string queryStringValue)
        {
            var request = TestFactory.CreateHttpRequest(queryStringKey, queryStringValue);
            var response = (OkObjectResult)await HttpTrigger.Run(request, logger);
            Assert.Equal($"Hello, {queryStringValue}", response.Value);
        }

        [Fact]
        public void Timer_should_log_message()
        {
            var logger = (ListLogger)TestFactory.CreateLogger(LoggerTypes.List);
            TimerTrigger.Run(null, logger);
            var msg = logger.Logs[0];
            Assert.Contains("C# Timer trigger function executed at", msg);
        }
    }
}

このクラスで実装されるメンバーは次のとおりです。The members implemented in this class are:

  • Http_trigger_should_return_known_string:このテストは、HTTP 関数へのクエリ文字列値 name=Bill を含む要求を作成し、予期された応答が返されることを確認します。Http_trigger_should_return_known_string: This test creates a request with the query string values of name=Bill to an HTTP function and checks that the expected response is returned.

  • Http_trigger_should_return_string_from_member_data:このテストは、xUnit 属性を使用して、HTTP 関数にサンプル データを提供します。Http_trigger_should_return_string_from_member_data: This test uses xUnit attributes to provide sample data to the HTTP function.

  • Timer_should_log_message:このテストは、ListLogger のインスタンスを作成してタイマー関数に渡します。Timer_should_log_message: This test creates an instance of ListLogger and passes it to a timer functions. 関数が実行されると、予期されたメッセージが存在することを確認するためにログがチェックされます。Once the function is run, then the log is checked to ensure the expected message is present.

テストでアプリケーション設定にアクセスする場合は、System.Environment.GetEnvironmentVariable を使用できます。If you want to access application settings in your tests, you can use System.Environment.GetEnvironmentVariable.

テストの実行Run tests

テストを実行するには、テスト エクスプローラーに移動し、 [すべて実行] の順にクリックします。To run the tests, navigate to the Test Explorer and click Run all.

Visual Studio で C# を使って Azure Functions をテストする

テストのデバッグDebug tests

テストをデバッグするには、テストにブレークポイントを設定し、テスト エクスプローラーに移動して、 [実行] > [前回の実行をデバッグする] の順にクリックします。To debug the tests, set a breakpoint on a test, navigate to the Test Explorer and click Run > Debug Last Run.

VS Code での JavaScriptJavaScript in VS Code

次の例では、VS Code で JavaScript 関数アプリを作成し、Jest を使用して実行およびテストする方法を説明します。The following example describes how to create a JavaScript Function app in VS Code and run and tests with Jest. この手順では、VS Code の Functions 拡張機能を使用して Azure Functions を作成します。This procedure uses the VS Code Functions extension to create Azure Functions.

VS Code で JavaScript を使って Azure Functions をテストする

セットアップSetup

環境を設定するには、npm init を実行して空のフォルダーで新しい Node.js アプリを初期化します。To set up your environment, initialize a new Node.js app in an empty folder by running npm init.

npm init -y

次に、次のコマンドを実行して Jest をインストールします。Next, install Jest by running the following command:

npm i jest

この時点で package.json を更新して、既存のテスト コマンドを次のコマンドに置き換えます。Now update package.json to replace the existing test command with the following command:

"scripts": {
    "test": "jest"
}

テスト モジュールを作成するCreate test modules

プロジェクトが初期化されたら、自動テストの実行に使用するモジュールを作成できます。With the project initialized, you can create the modules used to run the automated tests. 最初に、testing という名前の、サポート モジュールを保持する新しいフォルダーを作成します。Begin by creating a new folder named testing to hold the support modules.

testing フォルダーに新しいファイルを追加して defaultContext.js という名前を付け、次のコードを追加します。In the testing folder add a new file, name it defaultContext.js, and add the following code:

module.exports = {
    log: jest.fn()
};

このモジュールは、log 関数をモックして既定の実行コンテキストを表します。This module mocks the log function to represent the default execution context.

次に、新しいファイルを追加して defaultTimer.js という名前を付け、次のコードを追加します。Next, add a new file, name it defaultTimer.js, and add the following code:

module.exports = {
    IsPastDue: false
};

このモジュールは、偽のタイマー インスタンスである IsPastDue プロパティを実装します。This module implements the IsPastDue property to stand is as a fake timer instance. テスト ハーネスは結果をテストするために関数を直接呼び出すだけなので、ここでは NCRONTAB 式などのタイマー構成は必要ありません。Timer configurations like NCRONTAB expressions are not required here as the test harness is simply calling the function directly to test the outcome.

次に、VS Code の Functions 拡張機能を使用して、JavaScript HTTP 関数を新規作成して HttpTrigger という名前を付けます。Next, use the VS Code Functions extension to create a new JavaScript HTTP Function and name it HttpTrigger. 関数を作成した後、index.test.js という名前の同じフォルダーに新しいファイルを追加し、次のコードを追加します。Once the function is created, add a new file in the same folder named index.test.js, and add the following code:

const httpFunction = require('./index');
const context = require('../testing/defaultContext')

test('Http trigger should return known text', async () => {

    const request = {
        query: { name: 'Bill' }
    };

    await httpFunction(context, request);

    expect(context.log.mock.calls.length).toBe(1);
    expect(context.res.body).toEqual('Hello Bill');
});

テンプレートからの HTTP 関数は、クエリ文字列に指定された名前と連結された文字列 "Hello" を返します。The HTTP function from the template returns a string of "Hello" concatenated with the name provided in the query string. このテストは、要求の偽のインスタンスを作成して HTTP 関数に渡します。This test creates a fake instance of a request and passes it to the HTTP function. このテストは、log メソッドが 1 回呼び出され、返されるテキストが "Hello Bill" に等しいことを確認します。The test checks that the log method is called once and the returned text equals "Hello Bill".

次に、VS Code の Functions 拡張機能を使用して、JavaScript タイマー関数を新規作成して TimerTriggerという名前を付けます。Next, use the VS Code Functions extension to create a new JavaScript Timer Function and name it TimerTrigger. 関数を作成した後、index.test.js という名前の同じフォルダーに新しいファイルを追加し、次のコードを追加します。Once the function is created, add a new file in the same folder named index.test.js, and add the following code:

const timerFunction = require('./index');
const context = require('../testing/defaultContext');
const timer = require('../testing/defaultTimer');

test('Timer trigger should log message', () => {
    timerFunction(context, timer);
    expect(context.log.mock.calls.length).toBe(1);
});

テンプレートからのタイマー関数は、関数本文の最後のメッセージをログに記録します。The timer function from the template logs a message at the end of the body of the function. このテストは、log 関数が 1 回呼び出されることを確認します。This test ensures the log function is called once.

テストの実行Run tests

キーを実行するには、CTRL + ~ を押してコマンド ウィンドウを開き、npm test を実行します。To run the tests, press CTRL + ~ to open the command window, and run npm test:

npm test

VS Code で JavaScript を使って Azure Functions をテストする

テストのデバッグDebug tests

テストをデバッグするには、launch.json ファイルに次の構成を追加します。To debug your tests, add the following configuration to your launch.json file:

{
  "type": "node",
  "request": "launch",
  "name": "Jest Tests",
  "disableOptimisticBPs": true,
  "program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
  "args": [
      "-i"
  ],
  "internalConsoleOptions": "openOnSessionStart"
}

次に、テストにブレークポイントを設定し、F5 を押します。Next, set a breakpoint in your test and press F5.

次の手順Next steps

関数に対する自動テストを記述する方法を習得したので、以下のリソースに進みます。Now that you've learned how to write automated tests for your functions, continue with these resources: