Stratégies permettant de tester votre code dans Azure FunctionsStrategies for testing your code in Azure Functions

Cet article explique comment créer des tests automatisés pour Azure Functions.This article demonstrates how to create automated tests for Azure Functions.

Tester l'intégralité du code est recommandé. Cela étant, vous pouvez obtenir de meilleurs résultats en compilant une logique de fonction et en créant des tests en dehors de cette fonction.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. Faire abstraction d'une logique limite les lignes de code de la fonction et permet uniquement à cette dernière d'appeler d'autres classes ou modules.Abstracting logic away limits a Function's lines of code and allows the Function to be solely responsible for calling other classes or modules. Cet article, cependant, explique comment créer des tests automatisés pour des fonctions HTTP et déclenchées par un minuteur.This article, however, demonstrates how to create automated tests against an HTTP and timer-triggered functions.

Le contenu suivant est divisé en deux sections distinctes destinées à cibler différents environnements et langages.The content that follows is split into two different sections meant to target different languages and environments. Découvrez comment créer des tests dans :You can learn to build tests in:

L’exemple de dépôt est disponible sur GitHub.The sample repository is available on GitHub.

C# dans Visual StudioC# in Visual Studio

L’exemple suivant explique comment créer une application de fonction C# dans Visual Studio et exécuter des tests avec xUnit.The following example describes how to create a C# Function app in Visual Studio and run and tests with xUnit.

Tester Azure Functions avec C# dans Visual Studio

Programme d’installationSetup

Pour configurer votre environnement, créez une fonction et testez l’application.To set up your environment, create a Function and test app. Les étapes suivantes vous permettent de créer les applications et les fonctions requises pour prendre en charge les tests :The following steps help you create the apps and functions required to support the tests:

  1. Créez une nouvelle application Functions et nommez-la Functions.Create a new Functions app and name it Functions
  2. Créez une fonction HTTP à partir du modèle et nommez-la MyHttpTrigger.Create an HTTP function from the template and name it MyHttpTrigger.
  3. Créez une fonction de minuteur à partir du modèle et nommez-la MyTimerTrigger.Create a timer function from the template and name it MyTimerTrigger.
  4. Créez une application de test xUnit dans la solution et nommez-la Functions.Tests.Create an xUnit Test app in the solution and name it Functions.Tests.
  5. Utilisez NuGet pour ajouter des références à partir de l’application de test à Microsoft.AspNetCore.MvcUse NuGet to add a reference from the test app to Microsoft.AspNetCore.Mvc
  6. Faites référence à l’application Functions à partir de l’application Functions.Tests.Reference the Functions app from Functions.Tests app.

Créer des classes de testCreate test classes

Une fois les projets créés, vous pouvez créer les classes servant à exécuter les tests automatisés.Now that the projects are created, you can create the classes used to run the automated tests.

Chaque fonction utilise une instance ILogger pour gérer la journalisation des messages.Each function takes an instance of ILogger to handle message logging. Certains tests ne consignent pas les messages ou ne se soucient pas de la manière dont la journalisation est implémentée.Some tests either don't log messages or have no concern for how logging is implemented. D'autres tests doivent évaluer les messages consignés afin de déterminer si un test a abouti.Other tests need to evaluate messages logged to determine whether a test is passing.

Vous allez créer une nouvelle classe nommée ListLogger qui contient une liste interne de messages à évaluer pendant un test.You'll create a new class named ListLogger which holds an internal list of messages to evaluate during a testing. Pour implémenter l’interface ILogger requise, la classe a besoin d’une portée.To implement the required ILogger interface, the class needs a scope. La classe suivante imite une portée pour les cas de test à passer à la classe ListLogger.The following class mocks a scope for the test cases to pass to the ListLogger class.

Créez une classe nommée NullScope.cs dans le projet Functions.Tests et entrez le code suivant :Create a new class in Functions.Tests project named 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() { }
    }
}

Maintenant, créez une classe nommée ListLogger.cs dans le projet Functions.Tests et entrez le code suivant :Next, create a new class in Functions.Tests project named 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);
        }
    }
}

La classe ListLogger implémente les membres suivants comme contractés par l'interface ILogger :The ListLogger class implements the following members as contracted by the ILogger interface:

  • BeginScope : Les étendues ajoutent du contexte à votre journalisation.BeginScope: Scopes add context to your logging. Dans ce cas, le test pointe vers l’instance statique de la classe NullScope pour permettre le bon fonctionnement du test.In this case, the test just points to the static instance on the NullScope class to allow the test to function.

  • IsEnabled : La valeur par défaut false est indiquée.IsEnabled: A default value of false is provided.

  • Log : Cette méthode utilise la fonction formatter fournie pour mettre en forme le message, puis ajoute le texte qui en résulte à la collection Logs.Log: This method uses the provided formatter function to format the message and then adds the resulting text to the Logs collection.

La collection Logs est une instance List<string> initialisée dans le constructeur.The Logs collection is an instance of List<string> and is initialized in the constructor.

Maintenant, créez un fichier nommé LoggerTypes.cs dans le projet Functions.Tests et entrez le code suivant :Next, create a new file in Functions.Tests project named LoggerTypes.cs and enter the following code:

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

Cette énumération spécifie le type d'enregistreur d'événements utilisé par les tests.This enumeration specifies the type of logger used by the tests.

Maintenant, créez une classe nommée TestFactory.cs dans le projet Functions.Tests et entrez le code suivant :Now create a new class in Functions.Tests project named 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 HttpRequest CreateHttpRequest(string queryStringKey, string queryStringValue)
        {
            var context = new DefaultHttpContext();
            var request = context.Request;
            request.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;
        }
    }
}

La classe TestFactory implémente les membres suivants :The TestFactory class implements the following members:

  • Données : Cette propriété renvoie une collection IEnumerable d’exemples de données.Data: This property returns an IEnumerable collection of sample data. Les paires clé/valeur représentent les valeurs transmises dans une chaîne de requête.The key value pairs represent values that are passed into a query string.

  • CreateDictionary : Cette méthode accepte une paire clé/valeur en tant qu’arguments et renvoie une nouvelle Dictionary utilisée pour créer QueryCollection afin de représenter les valeurs de chaîne de requête.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 : Cette méthode crée une requête HTTP initialisée avec les paramètres d'une chaîne de requête donnée.CreateHttpRequest: This method creates an HTTP request initialized with the given query string parameters.

  • CreateLogger : En fonction du type d'enregistreur d’événements, cette méthode renvoie une classe d’enregistreur d’événements utilisée à des fins de test.CreateLogger: Based on the logger type, this method returns a logger class used for testing. Le ListLogger effectue le suivi des messages consignés disponibles à des fins d'évaluation lors des tests.The ListLogger keeps track of logged messages available for evaluation in tests.

Enfin, créez une classe nommée FunctionsTests.cs dans le projet Functions.Tests et entrez le code suivant :Finally, create a new class in Functions.Tests project named 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 MyHttpTrigger.Run(request, logger);
            Assert.Equal("Hello, Bill. This HTTP triggered function executed successfully.", 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 MyHttpTrigger.Run(request, logger);
            Assert.Equal($"Hello, {queryStringValue}. This HTTP triggered function executed successfully.", response.Value);
        }

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

Les membres implémentés dans cette classe sont :The members implemented in this class are:

  • Http_trigger_should_return_known_string : Ce test crée une requête avec les valeurs de chaîne de requête de name=Bill vers une fonction HTTP et vérifie que la réponse attendue est renvoyée.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 : Ce test utilise les attributs xUnit pour fournir des exemples de données à la fonction 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 : Ce test crée une instance de ListLogger et la transmet à des fonctions de minuteur.Timer_should_log_message: This test creates an instance of ListLogger and passes it to a timer functions. Une fois la fonction exécutée, le journal est vérifié pour veiller à ce que le message attendu soit présent.Once the function is run, then the log is checked to ensure the expected message is present.

Si vous souhaitez accéder aux paramètres d’application dans vos tests, vous pouvez utiliser System.Environment.GetEnvironmentVariable.If you want to access application settings in your tests, you can use System.Environment.GetEnvironmentVariable.

Exécuter les testsRun tests

Pour exécuter les tests, accédez à l'Explorateur de tests et cliquez sur Exécuter tout.To run the tests, navigate to the Test Explorer and click Run all.

Tester Azure Functions avec C# dans Visual Studio

Déboguer les testsDebug tests

Pour déboguer les tests, définissez un point d’arrêt sur un test, accédez à l'Explorateur de tests et cliquez sur Exécuter > Déboguer la dernière exécution.To debug the tests, set a breakpoint on a test, navigate to the Test Explorer and click Run > Debug Last Run.

JavaScript dans VS CodeJavaScript in VS Code

L’exemple suivant explique comment créer une application de fonction JavaScript dans VS Code, puis exécuter les tests avec Jest.The following example describes how to create a JavaScript Function app in VS Code and run and tests with Jest. Cette procédure utilise l'extension Fonctions VS Code pour créer Azure Functions.This procedure uses the VS Code Functions extension to create Azure Functions.

Tester Azure Functions avec JavaScript dans VS Code

Programme d’installationSetup

Pour configurer votre environnement, initialisez une application Node.js dans un dossier vide en exécutant npm init.To set up your environment, initialize a new Node.js app in an empty folder by running npm init.

npm init -y

Installez ensuite Jest en exécutant la commande suivante :Next, install Jest by running the following command:

npm i jest

À présent, mettez à jour package.json pour remplacer la commande de test existante par la commande suivante :Now update package.json to replace the existing test command with the following command:

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

Créer des modules de testCreate test modules

Une fois le projet initialisé, vous pouvez créer les modules utilisés pour exécuter les tests automatisés.With the project initialized, you can create the modules used to run the automated tests. Commencez par créer un nouveau dossier nommé testing pour contenir les modules de prise en charge.Begin by creating a new folder named testing to hold the support modules.

Dans le dossier testing, ajoutez un nouveau fichier, nommez-le defaultContext.js, puis ajoutez le code suivant :In the testing folder add a new file, name it defaultContext.js, and add the following code:

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

Ce module simule la fonction log pour représenter le contexte d’exécution par défaut.This module mocks the log function to represent the default execution context.

Ensuite, ajoutez un nouveau fichier, nommez-le defaultTimer.js et ajoutez le code suivant :Next, add a new file, name it defaultTimer.js, and add the following code:

module.exports = {
    IsPastDue: false
};

Ce module implémente la propriété IsPastDue en tant qu'instance fictive du minuteur.This module implements the IsPastDue property to stand is as a fake timer instance. Des configurations de minuteur telles que des expressions NCRONTAB ne sont pas requises ici, car l’atelier de test appelle simplement la fonction directement pour tester le résultat.Timer configurations like NCRONTAB expressions are not required here as the test harness is simply calling the function directly to test the outcome.

Ensuite, utilisez l’extension VS Code Functions pour créer une fonction HTTP JavaScript et nommez-la HttpTrigger.Next, use the VS Code Functions extension to create a new JavaScript HTTP Function and name it HttpTrigger. Une fois la fonction créée, ajoutez un nouveau fichier au même dossier nommé index.test.js et ajoutez le code suivant :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');
});

La fonction HTTP du modèle renvoie une chaîne « Hello » concaténée avec le nom fourni dans la chaîne de requête.The HTTP function from the template returns a string of "Hello" concatenated with the name provided in the query string. Ce test crée une instance fictive de requête et la transmet à la fonction HTTP.This test creates a fake instance of a request and passes it to the HTTP function. Le test vérifie que la méthode log est appelée et que le texte renvoyé indique « Hello Bill ».The test checks that the log method is called once and the returned text equals "Hello Bill".

Ensuite, utilisez l’extension VS Code Functions pour créer une fonction de minuteur JavaScript et nommez-la TimerTrigger.Next, use the VS Code Functions extension to create a new JavaScript Timer Function and name it TimerTrigger. Une fois la fonction créée, ajoutez un nouveau fichier au même dossier nommé index.test.js et ajoutez le code suivant :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);
});

La fonction de minuteur du modèle consigne un message à la fin du corps de la fonction.The timer function from the template logs a message at the end of the body of the function. Ce test permet de s'assurer que la fonction log est appelée.This test ensures the log function is called once.

Exécuter les testsRun tests

Pour exécuter les tests, appuyez sur CTRL + ~ afin d'ouvrir la fenêtre de commande, puis exécutez npm test :To run the tests, press CTRL + ~ to open the command window, and run npm test:

npm test

Tester Azure Functions avec JavaScript dans VS Code

Déboguer les testsDebug tests

Pour déboguer vos tests, ajoutez la configuration suivante à votre fichier 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"
}

Ensuite, définissez un point d’arrêt dans votre test et appuyez sur F5.Next, set a breakpoint in your test and press F5.

Étapes suivantesNext steps

Maintenant que vous savez écrire des tests automatisés pour vos fonctions, penchez-vous sur les ressources suivantes :Now that you've learned how to write automated tests for your functions, continue with these resources: