Estratégias para testar seu código no Azure FunctionsStrategies for testing your code in Azure Functions

Este artigo demonstra como criar testes automatizados para o Azure Functions.This article demonstrates how to create automated tests for Azure Functions.

É recomendável testar todo o código; no entanto, talvez você obtenha resultados melhores ao encapsular uma lógica da função e criar testes fora dela.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. Abstrair a lógica limita as linhas de código da função e permite que ela seja a única responsável por chamar outras classes ou módulos.Abstracting logic away limits a Function's lines of code and allows the Function to be solely responsible for calling other classes or modules. No entanto, este artigo demonstra como criar testes automatizados em um HTTP e uma função disparada por temporizador.This article, however, demonstrates how to create automated tests against an HTTP and timer-triggered function.

O conteúdo a seguir é dividido em duas seções diferentes direcionadas a linguagens e ambientes distintos.The content that follows is split into two different sections meant to target different languages and environments. Você pode aprender a criar testes:You can learn to build tests in:

O repositório de exemplo está disponível no GitHub.The sample repository is available on GitHub.

C# no Visual StudioC# in Visual Studio

O exemplo a seguir descreve como criar um aplicativo de funções C# no Visual Studio e executar testes com xUnit.The following example describes how to create a C# Function app in Visual Studio and run and tests with xUnit.

Testar o Azure Functions com C# no Visual Studio

InstalaçãoSetup

Para configurar o ambiente, crie uma função e teste o aplicativo.To set up your environment, create a Function and test app. As etapas a seguir ajudam você a criar os aplicativos e as funções necessários para os testes:The following steps help you create the apps and functions required to support the tests:

  1. Crie um aplicativo de funções e nomeie-o como FunctionsCreate a new Functions app and name it Functions
  2. Crie uma função HTTP do modelo e nomeie-a HttpTrigger.Create an HTTP function from the template and name it HttpTrigger.
  3. Crie uma função temporizadora do modelo e nomeie-a TimerTrigger.Create a timer function from the template and name it TimerTrigger.
  4. Crie um aplicativo de teste xUnit no Visual Studio, clicando em Arquivo > Novo > Projeto > Visual C# > .NET Core > Projeto de teste xUnit e nomeie-o 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. Use o NuGet para adicionar uma referência do aplicativo de teste Microsoft. AspNetCore. MvcUse Nuget to add a references from the test app Microsoft.AspNetCore.Mvc
  6. Referencie o aplicativo Functions no aplicativo Functions.Test.Reference the Functions app from Functions.Test app.

Criar classes de testeCreate test classes

Agora que os aplicativos foram criados, será possível criar as classes usadas para executar os testes automatizados.Now that the applications are created, you can create the classes used to run the automated tests.

Cada função usa uma instância do ILogger para manipular o log de mensagens.Each function takes an instance of ILogger to handle message logging. Alguns testes não registram mensagens ou não se preocupam com como log é implementado.Some tests either don't log messages or have no concern for how logging is implemented. Outros testes precisam avaliar as mensagens registradas para determinar se foram aprovadas.Other tests need to evaluate messages logged to determine whether a test is passing.

A classe ListLogger deve implementar a interface ILogger e manter uma lista interna de mensagens para avaliação durante um teste.The ListLogger class is meant to implement the ILogger interface and hold in internal list of messages for evaluation during a test.

Clique com o botão direito do mouse no aplicativo functions. Test e selecione Adicionar > classe, nomeie-o NullScope.cs e insira o código a seguir: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() { }
    }
}

Em seguida, clique com o botão direito do mouse no aplicativo functions. Test e selecione Adicionar > classe, nomeie-o ListLogger.cs e insira o código a seguir: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);
        }
    }
}

A classe ListLogger implementará os seguintes membros, conforme contratado pela interface ILogger:The ListLogger class implements the following members as contracted by the ILogger interface:

  • BeginScope: os escopos adicionam contexto ao seu registro em log.BeginScope: Scopes add context to your logging. Nesse caso, o teste apenas aponta para a instância estática na classe NullScope para permitir que o teste funcione.In this case, the test just points to the static instance on the NullScope class to allow the test to function.

  • IsEnabled: um valor padrão de false é fornecido.IsEnabled: A default value of false is provided.

  • Log: esse método usa a função de formatter fornecida para formatar a mensagem e, em seguida, adiciona o texto resultante à coleção de Logs.Log: This method uses the provided formatter function to format the message and then adds the resulting text to the Logs collection.

A coleção Logs é uma instância de List<string> e é inicializada no construtor.The Logs collection is an instance of List<string> and is initialized in the constructor.

Em seguida, clique com o botão direito do mouse no aplicativo Functions.Test e selecione Adicionar > Classe. Nomeie como LoggerTypes.cs e insira este código: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
    }
}

Esta enumeração especifica o tipo de agente usado pelos testes.This enumeration specifies the type of logger used by the tests.

Em seguida, clique com o botão direito do mouse no aplicativo Functions.Test e selecione Adicionar > Classe. Nomeie como TestFactory.cs e insira este código: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;
        }
    }
}

A classe TestFactory implementa os seguintes membros:The TestFactory class implements the following members:

  • Dados: essa propriedade retorna uma coleção IEnumerable de dados de exemplo.Data: This property returns an IEnumerable collection of sample data. Os pares chave-valor representam valores que são passados para uma cadeia de caracteres de consulta.The key value pairs represent values that are passed into a query string.

  • CreateDictionary: esse método aceita um par de chave/valor como argumentos e retorna um novo Dictionary usado para criar QueryCollection para representar valores de cadeia de caracteres de consulta.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: esse método cria uma solicitação HTTP inicializada com os parâmetros de cadeia de caracteres de consulta fornecidos.CreateHttpRequest: This method creates an HTTP request initialized with the given query string parameters.

  • Createlogger: com base no tipo de agente, esse método retorna uma classe de agente usada para teste.CreateLogger: Based on the logger type, this method returns a logger class used for testing. O ListLogger controla as mensagens registradas disponíveis para avaliação em testes.The ListLogger keeps track of logged messages available for evaluation in tests.

Em seguida, clique com o botão direito do mouse no aplicativo Functions.Test e selecione Adicionar > Classe. Nomeie como FunctionsTests.cs e insira este código: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);
        }
    }
}

Os membros implementados nesta classe são:The members implemented in this class are:

  • Http_trigger_should_return_known_string: esse teste cria uma solicitação com os valores de cadeia de caracteres de consulta de name=Bill para uma função http e verifica se a resposta esperada é retornada.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: esse teste usa atributos xUnit para fornecer dados de exemplo para a função 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: esse teste cria uma instância de ListLogger e a passa para uma função de temporizador.Timer_should_log_message: This test creates an instance of ListLogger and passes it to a timer functions. Depois que a função é executada, o log é verificado para garantir que a mensagem esperada está presente.Once the function is run, then the log is checked to ensure the expected message is present.

Se você quiser acessar as configurações do aplicativo em seus testes, você pode usar System. Environment. GetEnvironmentVariable.If you want to access application settings in your tests, you can use System.Environment.GetEnvironmentVariable.

Executar testesRun tests

Para executar os testes, navegue até a Gerenciador de Testes e clique em Executar todos.To run the tests, navigate to the Test Explorer and click Run all.

Testar o Azure Functions com C# no Visual Studio

Depurar testesDebug tests

Para depurar os testes, defina um ponto de interrupção em um teste, navegue até o Gerenciador de Testes e clique em Executar > Depurar última execução.To debug the tests, set a breakpoint on a test, navigate to the Test Explorer and click Run > Debug Last Run.

JavaScript no VS CodeJavaScript in VS Code

O exemplo a seguir descreve como criar um aplicativo de funções JavaScript no VS Code e executar testes com Jest.The following example describes how to create a JavaScript Function app in VS Code and run and tests with Jest. Este procedimento usa a extensão de funções do VS Code para criar no Azure Functions.This procedure uses the VS Code Functions extension to create Azure Functions.

Testar o Azure Functions com JavaScript no VS Code

InstalaçãoSetup

Para configurar o ambiente, inicialize um novo aplicativo Node.js em uma pasta vazia executando npm init.To set up your environment, initialize a new Node.js app in an empty folder by running npm init.

npm init -y

Em seguida, instale o Jest executando o seguinte comando:Next, install Jest by running the following command:

npm i jest

Agora, atualize package.json para substituir o comando de teste existente com o seguinte comando:Now update package.json to replace the existing test command with the following command:

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

Criar módulos de testeCreate test modules

Com o projeto inicializado, você poderá criar os módulos usados para executar os testes automatizados.With the project initialized, you can create the modules used to run the automated tests. Comece criando uma pasta chamada testing para manter os módulos de suporte.Begin by creating a new folder named testing to hold the support modules.

Na pasta testing, adicione um novo arquivo, nomeie-o como defaultContext.js e adicione o seguinte código:In the testing folder add a new file, name it defaultContext.js, and add the following code:

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

Esse módulo simula a função log para representar o contexto de execução padrão.This module mocks the log function to represent the default execution context.

Em seguida, adicione um novo arquivo, nomeie- defaultTimer.js e adicione o seguinte código:Next, add a new file, name it defaultTimer.js, and add the following code:

module.exports = {
    IsPastDue: false
};

Esse módulo implementa a propriedade IsPastDue como uma instância de temporizador falsa.This module implements the IsPastDue property to stand is as a fake timer instance. Configurações de temporizador como expressões NCRONTAB não são necessárias aqui, pois o equipamento de teste está simplesmente chamando a função diretamente para testar o resultado.Timer configurations like NCRONTAB expressions are not required here as the test harness is simply calling the function directly to test the outcome.

Em seguida, use a extensão de funções do VS Code para criar uma função HTTP do JavaScript e nomeie-a HttpTrigger.Next, use the VS Code Functions extension to create a new JavaScript HTTP Function and name it HttpTrigger. Após a criação da função, adicione um novo arquivo à mesma pasta de nome index.test.js e adicione o seguinte código: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');
});

A função HTTP do modelo retornará uma cadeia de caracteres "Hello" concatenada com o nome fornecido na cadeia de caracteres de consulta.The HTTP function from the template returns a string of "Hello" concatenated with the name provided in the query string. Esse teste cria uma instância falsa de uma solicitação e passa-a para a função HTTP.This test creates a fake instance of a request and passes it to the HTTP function. O teste verifica se o método log é chamado uma vez e se o texto retornado é igual a "Hello Bill".The test checks that the log method is called once and the returned text equals "Hello Bill".

Em seguida, use a extensão de funções do VS Code para criar uma função de temporizador do JavaScript e nomeie-a TimerTrigger.Next, use the VS Code Functions extension to create a new JavaScript Timer Function and name it TimerTrigger. Após a criação da função, adicione um novo arquivo à mesma pasta de nome index.test.js e adicione o seguinte código: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);
});

A função de temporizador do modelo registra uma mensagem no final do corpo da função.The timer function from the template logs a message at the end of the body of the function. Esse teste assegura que a função log será chamada uma vez.This test ensures the log function is called once.

Executar testesRun tests

Para executar os testes, pressione CTRL + ~ para abrir a janela de comando e execute npm test:To run the tests, press CTRL + ~ to open the command window, and run npm test:

npm test

Testar o Azure Functions com JavaScript no VS Code

Depurar testesDebug tests

Para depurar seus testes, adicione a seguinte configuração ao arquivo 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"
}

Em seguida, defina um ponto de interrupção no teste e pressione F5.Next, set a breakpoint in your test and press F5.

Próximas etapasNext steps

Agora que você aprendeu a gravar testes automatizados das funções, continue com estes recursos:Now that you've learned how to write automated tests for your functions, continue with these resources: