Strategie per il test del codice in Funzioni di Azure

Questo articolo illustra come creare i test automatizzati per le Funzioni di Azure.

Si consiglia di testare tutto il codice, tuttavia si potrebbero ottenere risultati migliori eseguendo il wrapping di una logica della funzione e creando dei test all'esterno della funzione. Eseguendo subito l'astrazione per la logica si limitano le righe di una funzione di codice e si consente alla funzione di assumere la piena responsabilità per la chiamata ad altre classi o moduli. Questo articolo, tuttavia, illustra come creare test automatizzati su un HTTP e funzioni attivate da timer.

Il contenuto seguente è suddiviso in due sezioni diverse con lo scopo di ottenere diversi linguaggi e ambienti di destinazione. È possibile imparare a compilare i test in:

Il repository di esempio è disponibile in GitHub.

C# in Visual Studio

Il seguente esempio illustra come creare un'app della funzione C# in Visual Studio ed eseguire i test con xUnit.

Testing Azure Functions with C# in Visual Studio

Eseguire la configurazione

Per configurare l'ambiente, creare una funzione e testare l'app. I passaggi seguenti consentono di creare le app e le funzioni necessarie per supportare i test:

  1. Creare una nuova app funzioni e denominarla Funzioni
  2. Creare una funzione HTTP dal modello e assegnarle il nome MyHttpTrigger.
  3. Creare una funzione timer dal modello e assegnarle il nome MyTimerTrigger.
  4. Creare un'app xUnit Test nella soluzione e assegnare all'app il nome Functions.Tests.
  5. Usare NuGet per aggiungere un riferimento dall'app di test a Microsoft.AspNetCore.Mvc
  6. Fare riferimento all'app Funzionidall'app Functions.Tests.

Crea classi di test

Ora che i progetti sono stati creati, è possibile creare le classi usate per eseguire i test automatizzati.

Ogni funzione accetta un'istanza di ILogger per gestire la registrazione dei messaggi. Alcuni test non registrano messaggi oppure non hanno alcun problema per la modalità di implementazione della registrazione. Altri test devono valutare i messaggi registrati per determinare se si sta passando un test.

Si creerà una nuova classe denominata ListLogger che contiene un elenco interno di messaggi da valutare durante un test. Per implementare ILogger l'interfaccia richiesta, la classe richiede un ambito. La classe seguente fittizia un ambito per i test case da passare alla ListLogger classe .

Creare una nuova classe nel progetto Functions.Tests denominata NullScope.cs e immettere il codice seguente:

using System;

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

        private NullScope() { }

        public void Dispose() { }
    }
}

Creare quindi una nuova classe nel progetto Functions.Tests denominata ListLogger.cs e immettere il codice seguente:

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 implementa i membri seguenti come contrattato dall'interfaccia ILogger:

  • BeginScope:gli ambiti aggiungono contesto alla registrazione. In questo caso, il test punta solo all'istanza statica nella classe per NullScope consentire il funzionamento del test.

  • IsEnabled:viene fornito il valore predefinito .

  • Log:questo metodo usa la funzione fornita per formattare il messaggio e quindi aggiunge il testo risultante alla Logs raccolta.

La raccolta Logs è un'istanza di List<string> e viene inizializzata nel costruttore.

Creare quindi un nuovo file nel progetto Functions.Tests denominato LoggerTypes.cs e immettere il codice seguente:

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

Questa enumerazione specifica il tipo di logger usato dai test.

Creare ora una nuova classe nel progetto Functions.Tests denominata TestFactory.cs e immettere il codice seguente:

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 implementa i seguenti membri:

  • Data:questa proprietà restituisce una raccolta IEnumerable di dati di esempio. Le coppie chiave-valore rappresentano valori che vengono passati in una stringa di query.

  • CreateDictionary:questo metodo accetta una coppia chiave/valore come argomenti e restituisce un nuovo oggetto usato per creare per rappresentare i valori della stringa di QueryCollection query.

  • CreateHttpRequest:questo metodo crea una richiesta HTTP inizializzata con i parametri della stringa di query specificati.

  • CreateLogger:in base al tipo di logger, questo metodo restituisce una classe logger usata per il test. ListLogger tiene traccia dei messaggi registrati disponibili per la valutazione nei test.

Infine, creare una nuova classe nel progetto Functions.Tests denominata FunctionsTests.cs e immettere il codice seguente:

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);
        }
    }
}

I membri implementati in questa classe sono:

  • Http_trigger_should_return_known_string:questo test crea una richiesta con i valori della stringa di query di a una funzione HTTP e verifica che la risposta prevista sia restituita.

  • Http_trigger_should_return_string_from_member_data:questo test usa attributi xUnit per fornire dati di esempio alla funzione HTTP.

  • Timer_should_log_message:questo test crea un'istanza di e la passa a una funzione timer. Una volta eseguita la funzione, il log viene controllato per verificare che sia presente il messaggio previsto.

Se si vuole accedere alle impostazioni dell'applicazione nei test, è possibile inserire un'istanza con valori di variabili di ambiente fittizi nella funzione.

Esecuzione dei test

Per eseguire i test, passare al Explorer di Test e fare clic su Esegui tutto.

Testing Azure Functions with C# in Visual Studio

Esecuzione del debug dei test

Per eseguire il debug dei test, impostare un punto di interruzione in un test, passare a Esplora test e fare clic su Esegui debug ultima esecuzione.

JavaScript in Visual Studio Code

Il seguente esempio illustra come creare un'app della funzione di JavaScript in Visual Studio Code ed eseguire i test con Jest. Questa procedura usa l'estensione delle funzioni di Visual Studio Code per creare le Funzioni di Azure.

Testing Azure Functions with JavaScript in VS Code

Eseguire la configurazione

Per configurare l'ambiente, inizializzare una nuova app Node.js in una cartella vuota eseguendo npm init.

npm init -y

In seguito, installare Jest eseguendo il comando seguente:

npm i jest

A questo punto aggiornare package. JSON per sostituire il comando test esistente con il comando seguente:

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

Creare moduli del test

Con il progetto inizializzato, è possibile creare i moduli usati per eseguire i test automatizzati. Iniziare creando una nuova cartella denominata testing per contenere i moduli di supporto.

Nella cartella testing aggiungere un nuovo file, denominarla defaultContext.jse aggiungere il codice seguente:

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

Questo modulo simula la funzione log per rappresentare il contesto di esecuzione predefinito.

Successivamente, aggiungere un nuovo file, denominarlo defaultTimer.jse aggiungere il codice seguente:

module.exports = {
    IsPastDue: false
};

Questo modulo implementa la proprietà IsPastDue per realizzare che è come un'istanza del timer fittizia. Le configurazioni timer come le espressioni NCRONTAB non sono necessarie in questo caso perché test harness chiama semplicemente la funzione direttamente per testare il risultato.

Successivamente, usare l'estensione delle Funzioni di Visual Studio Code per creare una nuova funzione HTTP JavaScript e denominarla HttpTrigger. Dopo aver creato la funzione, aggiungere un nuovo file nella stessa cartella denominata index.test.js e aggiungere il codice seguente:

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 funzione HTTP dal modello restituisce una stringa di "Hello" concatenati con il nome specificato nella stringa di query. Questo test consente di creare un'istanza fittizia di una richiesta e la passa alla funzione HTTP. Il test verifica che il metodo log venga chiamato una sola volta e che il testo restituito sia uguale a "Hello Bill".

Successivamente, usare l'estensione delle Funzioni di Visual Studio Code per creare una nuova funzione timer JavaScript e denominarla TimerTrigger. Dopo aver creato la funzione, aggiungere un nuovo file nella stessa cartella denominata index.test.js e aggiungere il codice seguente:

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 funzione timer dal modello registra un messaggio alla fine del corpo della funzione. Questo test assicura che la funzione log venga chiamata una sola volta.

Esecuzione dei test

Per eseguire i test, premere CTRL + ~ per aprire la finestra di comando ed eseguire :

npm test

Testing Azure Functions with JavaScript in VS Code

Esecuzione del debug dei test

Per eseguire il debug dei test, aggiungere la configurazione seguente per il file launch.json file:

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

Successivamente, impostare un punto di interruzione del test e premere F5.

Passaggi successivi

Ora che si è appreso come scrivere i test automatizzati per le funzioni, continuare con queste risorse: