你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用 Visual Studio 开发 Azure Functions

使用 Visual Studio 可以开发、测试 C# 类库函数并将其部署到 Azure。 如果此体验是你第一次使用 Azure Functions,请参阅 Azure Functions 概述

若要立即开始,请考虑完成 Visual Studio 的函数快速入门

本文详细介绍了如何使用 Visual Studio 开发 C# 类库函数并将其发布到 Azure。 有两个模型用于开发 C# 类库函数: 独立辅助角色模型进程内模型

你正在阅读此文章的独立工作者模型版本。 可以在文章顶部选择首选模型。

你正在阅读本文中的进程内模型版本。 可以在文章顶部选择首选模型。

除非另有说明,否则将演示 Visual Studio 2022 的过程和示例。 有关 Visual Studio 2022 版本的详细信息,请参阅 发行说明预览发行说明

Prerequisites

  • Visual Studio 2022,包括 Azure 开发工作负载

  • 所需的其他资源(例如 Azure 存储帐户)将在发布过程中在订阅中创建。

  • 如果没有 Azure 帐户,请在开始前创建一个免费帐户

创建 Azure Functions 项目

Visual Studio 中的 Azure Functions 项目模板创建了一个 C# 类库项目,该项目可发布到 Azure 中的函数应用。 可使用函数应用将函数分组为逻辑单元,以便更轻松地管理、部署、缩放和共享资源。

  1. 在 Visual Studio 菜单中,选择“文件”>“新建”>“项目”

  2. 在“ 创建新项目 ”对话框中,在搜索框中输入 函数 ,选择 Azure Functions 模板,然后选择“ 下一步”。

  3. “配置新项目 ”对话框中,对于 “项目名称”,输入项目的名称,然后选择“ 下一步”。 函数应用名称必须可以充当 C# 命名空间,因此请勿使用下划线、连字符或任何其他的非字母数字字符。

  4. 在“ 其他信息 ”对话框中,执行下表中列出的作:

    设置 Action 说明
    Functions 辅助角色 选择.NET 8.0 独立(长期支持) Visual Studio 创建一个在 独立工作进程中运行的函数项目。 隔离的工作进程还支持其他不提供长期支持(LTS)的 .NET 和 .NET Framework 版本。 有关详细信息,请参阅 Azure Functions 运行时版本概述
    功能 选择 Http 触发器 Visual Studio 创建由 HTTP 请求触发的函数。
    将 Azurite 用于运行时存储账户 (AzureWebJobsStorage) 选中此复选框。 由于 Azure 中的函数应用需要存储帐户,因此在将项目发布到 Azure 时会分配或创建一个存储帐户。 HTTP 触发器不使用存储帐户连接字符串。 所有其他触发器类型都需要有效的存储帐户连接字符串。
    授权级别 选择 “匿名”。 使用此授权设置时,任何客户端都可以触发创建的函数,而无需提供密钥。 通过此配置,可以轻松测试新函数。 有关详细信息,请参阅授权级别

    Visual Studio“其他信息”对话框的屏幕截图,其中显示了为 Functions 辅助角色配置的设置,例如隔离的 .NET 版本。

    设置 Action 说明
    Functions 辅助角色 选择 .NET 8.0 进程内托管(长期支持)。 Visual Studio 创建一个函数项目,该项目使用 4.x 版 Functions 运行时运行进程内。 有关详细信息,请参阅 Azure Functions 运行时版本概述
    功能 选择 Http 触发器 Visual Studio 创建由 HTTP 请求触发的函数。
    将 Azurite 用于运行时存储账户 (AzureWebJobsStorage) 选中此复选框。 由于 Azure 中的函数应用需要存储帐户,因此在将项目发布到 Azure 时会分配或创建一个存储帐户。 HTTP 触发器不使用存储帐户连接字符串。 所有其他触发器类型都需要有效的存储帐户连接字符串。
    授权级别 选择 匿名 使用此授权设置时,任何客户端都可以触发创建的函数,而无需提供密钥。 通过此配置,可以轻松测试新函数。 有关详细信息,请参阅授权级别

    Visual Studio“其他信息”对话框的屏幕截图,其中显示了为 Functions 辅助角色配置的设置,例如进程内 .NET 版本。

    请确保将“授权级别”设置为“匿名”。 如果选择函数的默认 级别,则需要在请求中显示 函数密钥 才能访问函数终结点。

  5. 选择“创建”以创建函数项目和 HTTP 触发器函数

创建 Functions 项目后,项目模板将创建 C# 项目,安装 Microsoft.Azure.Functions.Worker NuGet Microsoft.Azure.Functions.Worker.Sdk 包并设置目标框架。

创建 Functions 项目后,项目模板将创建 C# 项目,安装 Microsoft.NET.Sdk.Functions NuGet 包并设置目标框架。

新项目包含以下文件:

  • host.json:此文件提供了配置 Functions 主机的方法。 在本地和 Azure 中运行时,都会应用这些设置。 有关详细信息,请参阅 host.json 参考

  • local.settings.json:此文件维护在本地运行函数时使用的设置。 应用在 Azure 中运行时不会使用这些设置。 有关详细信息,请参阅 在本地使用应用设置

    重要说明

    由于 local.settings.json 文件可以包含机密,因此必须将其从项目源代码管理中排除。 在此文件的“属性”对话框中,确保“复制到输出目录”设置为“如果较新则复制”

有关详细信息,请参阅独立工作者指南中的 项目结构

有关详细信息,请参阅 Functions 类库项目

在本地使用应用设置

函数应用在 Azure 中运行时,函数所需的设置存储在 应用设置中加密。 在本地开发期间,这些设置将改为添加到 local.settings.json 文件中的 Values 集合。 local.settings.json 文件还存储本地开发工具使用的设置。

项目 local.settings.json 文件中 Values 集合中的项旨在镜像 Azure 中函数应用的应用程序设置中的项。

发布项目时,Visual Studio 不会自动上传 local.settings.json 中的设置。 为了确保这些设置也存在于 Azure 的函数应用中,请在发布项目之后上传它们。 有关详细信息,请参阅函数应用设置。 集合中的 ConnectionStrings 值不会发布。

还可以在代码中将函数应用设置值读取为环境变量。 有关详细信息,请参阅环境变量

为本地开发配置项目

Functions 运行时在内部使用存储帐户。 在开发过程中,可以使用此内部帐户的有效存储帐户,也可以使用 Azurite 模拟器

对于 HTTP 和 Webhook 以外的所有触发器类型,需要在 Values.AzureWebJobsStorage 文件中设置密钥的值

  • 对于存储帐户,请将该值设置为存储帐户的连接字符串。
  • 对于模拟器,请将值设置为 UseDevelopmentStorage=true.

如果使用模拟器,请将此设置更改为部署前的实际存储帐户连接字符串。 有关详细信息,请参阅本地存储模拟器

若要设置存储帐户连接字符串,请执行以下步骤:

  1. 登录到 Azure 门户,然后转到存储帐户。

  2. 选择 “安全性 + 网络>访问密钥”。 在 key1 下,复制 连接字符串 值。

  3. 在 Visual Studio 项目中,打开 local.settings.json 文件。 将 AzureWebJobsStorage 键的值设置为复制的连接字符串。

  4. 重复上一步骤,将唯一键添加到函数所需的其他任何连接的 Values 数组。

将函数添加到项目

在 C# 类库函数中,函数使用的绑定是通过在代码中应用属性定义的。 从提供的模板创建函数触发器时,将为你应用触发器属性。

  1. 在“解决方案资源管理器”中,右键单击你的项目节点,然后选择“添加”“新建 Azure 函数”。>

  2. 在“ 添加新项 ”对话框中,选择 “Azure 函数”,然后选择“ 添加”。

  3. 选择触发器,然后设置所需的绑定属性。 如果选择存储服务触发器并想要配置连接,请选中用于配置触发器连接的复选框。 以下示例显示了用于创建队列存储触发器函数的设置。

    队列触发器的“新建 Azure 函数”对话框的屏幕截图,其中连接字符串名称设置为 QueueStorage,队列设置为 myqueue-items。

  4. 选择 并添加。 如果在上一步中选择用于配置存储连接的复选框,将显示 “连接到依赖项 ”页。 选择 Azurite 存储模拟器或 Azure 存储,然后选择“ 下一步”。

    • 如果选择 Azurite 存储模拟器,将显示 “连接到存储 Azurite 模拟器 ”页。 执行以下步骤:
      1. 选择“下一步”。
      2. “更改摘要 ”页上,选择“ 完成”。 Visual Studio 配置依赖项并创建触发器类。
    • 如果选择 Azure 存储,将显示 “连接到 Azure 存储 ”页。 执行以下步骤:
      1. 选择存储帐户,然后选择“ 下一步”。 Visual Studio 尝试连接到 Azure 帐户并检索终结点。
      2. 选择“下一步”。
      3. “更改摘要 ”页上,选择“ 完成”。 Visual Studio 配置依赖项并创建触发器类。

    此触发器示例使用应用程序设置与名为 QueueStorage 的密钥建立存储连接。 此密钥存储在 local.settings.json 文件中,引用 Azurite 模拟器或存储帐户。

  5. 检查新添加的类。 例如,以下 C# 类表示基本的队列存储触发器函数:

    Run() 方法具有 Function 属性。 该属性指示该方法是函数的入口点。

    using System;
    using Azure.Storage.Queues.Models;
    using Microsoft.Azure.Functions.Worker;
    using Microsoft.Extensions.Logging;
    
    namespace Company.Function;
    
    public class QueueTriggerCSharp
    {
        private readonly ILogger<QueueTriggerCSharp> _logger;
    
        public QueueTriggerCSharp(ILogger<QueueTriggerCSharp> logger)
        {
            _logger = logger;
        }
    
        [Function(nameof(QueueTriggerCSharp))]
        public void Run([QueueTrigger("PathValue", Connection = "ConnectionValue")] QueueMessage message)
        {
            _logger.LogInformation("C# Queue trigger function processed: {messageText}", message.MessageText);
        }
    }
    

    静态 Run() 方法被标注为 FunctionName. 该属性指示该方法是函数的入口点。

    using System;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Host;
    using Microsoft.Extensions.Logging;
    
    namespace Company.Function
    {
        public class QueueTriggerCSharp
        {
            [FunctionName("QueueTriggerCSharp")]
            public void Run([QueueTrigger("PathValue", Connection = "ConnectionValue")]string myQueueItem, ILogger log)
            {
                log.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
            }
        }
    }
    

已向提供给入口点方法的每个绑定参数提供了特定于绑定的属性。 该属性采用绑定信息作为参数。

在前面的代码中,第一个参数应用了一个 QueueTrigger 属性,该属性指示队列存储触发器函数。 队列名称和连接字符串设置名称作为参数传递给 QueueTrigger 属性。 在课堂中:

  • 队列名称参数应与在前面的步骤中使用的队列名称匹配,以创建触发器,例如 myqueue-items
  • 连接字符串设置名称应与在前面的步骤中使用的连接字符串设置名称匹配以创建触发器,例如 QueueStorage

有关详细信息,请参阅 Azure Functions 的 Azure 队列存储触发器

使用前面的过程向函数应用项目添加更多函数。 项目中的每个函数可以有不同的触发器,但每个函数的触发器必须刚好一个。 有关详细信息,请参阅 Azure Functions 触发器和绑定

添加绑定

使用触发器时,输入和输出绑定是作为绑定属性添加到函数的。 若要向函数添加绑定,请执行以下步骤:

  1. 确保已为本地开发配置项目

  2. 为每个特定绑定添加相应的 NuGet 扩展包。 有关特定于绑定的 NuGet 包要求,请查看关于绑定的参考文章。 例如,需要了解有关 Azure 事件中心触发器的软件包要求,请参阅 用于 Azure Functions 的 Azure 事件中心触发器和绑定

  3. 在包管理器控制台中使用以下命令安装特定包:

    Install-Package Microsoft.Azure.Functions.Worker.Extensions.<BINDING_TYPE> -Version <TARGET_VERSION>
    
    Install-Package Microsoft.Azure.WebJobs.Extensions.<BINDING_TYPE> -Version <TARGET_VERSION>
    

    在此代码中,将 <BINDING_TYPE> 替换为绑定扩展的特定名称,且将 <TARGET_VERSION> 替换为特定版本的包,例如 4.0.0。 在 NuGet.org 上的单个包页上列出了有效版本。

  4. 如果有绑定所需的应用设置,请将其添加到Values中的 集合。

    函数在本地运行时将使用这些值。 当函数在 Azure 的函数应用中运行时,它将使用函数应用设置。 使用 Visual Studio 可以轻松地将本地设置发布到 Azure

  5. 将适当的绑定属性添加到方法签名。 在以下代码中,队列消息触发函数 Run 。 然后,输出绑定会在不同的队列中创建具有相同文本的新队列消息。

     public class QueueTrigger
    {
        private readonly ILogger _logger;
    
        public QueueTrigger(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<QueueTrigger>();
        }
    
        [Function("CopyQueueMessage")]
        [QueueOutput("myqueue-items-destination", Connection = "QueueStorage")]
        public string Run([QueueTrigger("myqueue-items-source", Connection = "QueueStorage")] string myQueueItem)
        {
            _logger.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
            return myQueueItem;
        }
    }
    

    QueueOutput 属性定义了方法上的绑定。 对于多个输出绑定,请改为将此属性置于返回对象的字符串属性上。 有关详细信息,请参阅多个输出绑定

    public static class SimpleExampleWithOutput
    {
        [FunctionName("CopyQueueMessage")]
        public static void Run(
            [QueueTrigger("myqueue-items-source", Connection = "QueueStorage")] string myQueueItem, 
            [Queue("myqueue-items-destination", Connection = "QueueStorage")] out string myQueueItemCopy,
            ILogger log)
        {
            log.LogInformation($"CopyQueueMessage function processed: {myQueueItem}");
            myQueueItemCopy = myQueueItem;
        }
    }
    

    Queue 参数上的 out 属性定义了输出绑定。

    到队列存储的连接是通过 QueueStorage 设置获取的。 有关详细信息,请参阅特定绑定的参考文章。

有关 Functions 支持的绑定的完整列表,请参阅支持的绑定。 有关此方案更完整的示例,请参阅 使用 Visual Studio 将函数连接到 Azure 存储

在本地运行函数

可以使用 Azure Functions Core Tools 在本地开发计算机上运行 Functions 项目。 选择 F5 调试 Functions 项目时,本地 Functions 主机(func.exe)开始侦听本地端口(通常为 7071)。 任何可调用的函数终结点都将写入输出,你可以使用这些终结点来测试函数。 有关详细信息,请参阅 使用 Core Tools 在本地开发 Azure Functions。 当你首次从 Visual Studio 启动某个函数时,系统会提示你安装这些工具。

重要说明

从 Core Tools 版本 4.0.6517 开始,进程内模型项目必须引用 4.5.0 或更高版本的 Microsoft.NET.Sdk.Functions。 如果使用早期版本,该 func start 命令将生成错误。

若要在调试模式下在 Visual Studio 中启动函数,请执行以下步骤:

  1. 选择 F5。 如果系统提示,请接受来自 Visual Studio 的请求以下载并安装 Azure Functions Core Tools。 可能还需要打开防火墙例外,以便工具可以处理 HTTP 请求。

  2. 项目运行时,测试代码的方式与测试已部署的函数的方式相同。

    在调试模式下运行 Visual Studio 时,将会按预期命中断点。

有关使用 Visual Studio 的更详细的测试方案,请参阅本文后面的 测试函数

发布到 Azure

将 Functions 项目发布到 Azure 时,Visual Studio 使用 zip 部署 来部署项目文件。 如果可能,还应选择 “从包文件运行 ”,以便项目在部署(.zip)包中运行。 有关详细信息,请参阅从 Azure 中的包文件运行函数

不要使用 Web 部署 (msdeploy) 部署到 Functions。

使用以下步骤将项目发布到 Azure 中的函数应用:

  1. 解决方案资源管理器中,右键单击该项目,然后选择“ 发布”。

  2. “发布 ”页上,进行以下选择:

    • “目标”上,选择 “Azure”,然后选择“ 下一步”。
    • “特定目标”上,选择 “Azure Function App”,然后选择“ 下一步”。
    • Functions 实例上,选择“ 新建”。

    “发布”页的屏幕截图。在“Functions 实例”部分中,资源组可见,并突出显示了“新建”。

  3. 使用下表中指定的值创建新实例:

    设置 说明
    名称 全局唯一名称 该名称必须唯一标识新的函数应用。 接受建议的名称或输入新名称。 以下字符有效: a-z0-9-
    订阅名称 订阅的名称 函数应用是在 Azure 订阅中创建的。 接受默认订阅或从列表中选择其他订阅。
    资源组 资源组的名称 函数应用在资源组中创建。 选择“新建”来创建一个新的资源组。 还可以从列表中选择现有的资源组。
    计划类型 弹性消耗 将项目发布到运行于 Flex Consumption 计划 的函数应用时,您可能只需为函数应用的执行付费。 其他托管计划可能会产生更高的成本。
    重要提示:
    创建弹性消耗计划时,必须先选择 应用服务计划 ,然后重新选择 Flex Consumption 以清除对话框的问题。
    操作系统 Linux Flex 消耗计划当前需要 Linux。
    位置 应用服务的位置 选择 弹性消耗计划支持的 Azure 区域中的位置。 选择不受支持的区域时, “创建 ”按钮灰显。
    实例内存大小 2048 运行应用的 虚拟机实例的内存大小 对于 Flex Consumption 计划是唯一的。
    Azure 存储 常规用途存储帐户 Functions 运行时需要存储帐户。 选择“新建”即可配置常规用途存储帐户。 还可以使用满足 存储帐户要求的现有帐户
    Application Insights Application Insights 实例 应为函数应用启用 Application Insights 集成。 选择“新建”以在新的或现有的 Log Analytics 工作区中创建新实例。 还可以使用现有实例。

    “函数应用创建新”对话框的屏幕截图。填写名称、订阅、资源组、计划和其他设置的字段。

  4. 选择“创建”,在 Azure 中创建一个函数应用及其相关资源。 资源创建的状态显示在窗口左下角。

  5. 选择完成。 此时会显示 “发布配置文件创建进度 ”窗口。 创建配置文件时,选择“ 关闭”。

  6. 在发布配置文件页上,选择 “发布 ”将包含项目文件的包部署到 Azure 中的新函数应用。

    部署完成后,Azure 中的函数应用的根 URL 将显示在发布配置文件页上。

  7. 在发布配置文件页上,导航至“主机托管”部分。 选择省略号 (...),然后选择“ 在 Azure 门户中打开”。 新的函数应用 Azure 资源将在 Azure 门户中打开。

    “发布配置文件”页的屏幕截图。在“托管”部分,省略号快捷菜单已打开,且 Azure 门户中的“打开”按钮突出显示。

函数应用设置

发布项目时,Visual Studio 不会自动上传应用设置。 如果将设置添加到 local.settings.json 文件,还必须将它们添加到 Azure 中的函数应用。

将所需设置上传到 Azure 中的函数应用的最简单方法是在 Visual Studio 中管理这些设置。 在发布配置文件页上,导航至“主机托管”部分。 选择省略号(...),然后选择“ 管理 Azure 应用服务设置”。

发布配置文件页“托管”部分的屏幕截图。省略号快捷菜单处于打开状态,并突出显示了“管理 Azure 应用服务设置”。

进行选择时,将打开函数应用的 “应用程序设置 ”对话框。 可以使用此对话框添加应用程序设置或修改现有设置。

应用程序设置对话框的屏幕截图,其中显示了各种设置的本地值和远程值,以及用于添加和编辑值的控件。

对于每个设置, Local 值是 local.settings.json 文件中的值, 远程 值是 Azure 中函数应用中的值。

  • 若要创建应用设置,请选择“ 添加设置”。
  • 若要将设置值从 “本地 ”字段复制到 “远程 ”字段,请选择“ 本地”中的“插入”值

你选择“确定”后,挂起的更改将写入本地设置文件和函数应用。

注意

默认情况下,local.settings.json 文件不会提交到源代码管理。 因此,如果从源代码管理克隆本地 Functions 项目,则项目没有 local.settings.json 文件。 需要在项目根目录中手动创建 local.settings.json 文件,以便 应用程序设置 对话框按预期工作。

还可以采用以下这些其他方法之一来管理应用程序设置:

远程调试

要远程调试函数应用,必须发布项目的调试配置。 还需要在 Azure 中的函数应用中启用远程调试。

本部分假定函数应用的调试配置已发布。

远程调试注意事项

  • 不建议对生产服务进行远程调试。
  • 若要使用远程调试,必须在高级或应用服务计划中托管函数应用。
  • 当前仅在 Windows 上运行 C# 应用时支持远程调试。
  • 如果在 Visual Studio 中打开了“仅我的代码”功能,请将其关闭。 有关说明,请参阅 “启用或禁用仅我的代码”。
  • 使用远程调试时,避免在断点处长时间停止。 当进程停止的时间超过几分钟时,Azure 会将其视为无响应的进程,并将其关闭。
  • 调试时,服务器将数据发送到 Visual Studio,这可能会影响带宽费用。 有关带宽速率的信息,请参阅 定价计算器
  • 48 小时后,函数应用中会自动关闭远程调试。 在该点之后,需要重新打开远程调试。

附加调试程序

调试隔离的工作进程应用时,当前需要将远程调试器附加到单独的 .NET 进程。 还需要执行其他几个配置步骤。

若要将远程调试器附加到独立于 Functions 主机的进程中运行的函数应用,请执行以下步骤:

  1. 在发布配置文件页上,导航至“主机托管”部分。 选择省略号 (...),然后选择 “附加调试器”。

    Visual Studio 连接到您的函数应用程序,并打开远程调试(如果尚未开启)。

    注意

    由于远程调试器无法连接到主机进程,因此可能会出现错误消息。 在任何情况下,本地调试器都无法访问断点,也无法提供检查变量或逐步执行代码的方法。

  2. 在 Visual Studio 调试 菜单上,选择“ 附加到进程”。

  3. 在“ 附加到进程 ”对话框中,执行以下步骤:

    1. “连接类型”旁边,选择 Microsoft Azure 应用服务
    2. “连接目标”旁边,选择“ 查找”。
  4. 在“Azure 附加到进程”对话框中,搜索你的函数应用并将其选中,然后选择“确定”。

  5. 如果系统提示,请允许通过本地防火墙访问 Visual Studio。

  6. 返回“ 附加到进程 ”对话框,为 所有用户选择“显示进程”。 选择 dotnet.exe,然后选择 “附加”。

    “附加到进程”对话框的屏幕截图。连接类型Microsoft Azure 应用服务。在进程表中,选择 dotnet.exe。

操作完成后,会附加到在隔离工作进程中运行的 C# 类库代码。 此时,你可正常调试函数应用。

若要使用 Functions 主机将远程调试器附加到进程内运行的函数应用,请执行以下步骤。

在发布配置文件页上,导航至“主机托管”部分。 选择省略号 (...),然后选择 “附加调试器”。

Visual Studio 连接到您的函数应用程序,并打开远程调试(如果尚未开启)。 它还查找调试器并将其附加到应用的主机进程。 此时,你可正常调试函数应用。

完成调试后,应 关闭远程调试

关闭远程调试

完成代码远程调试后,应在 Azure 门户中关闭远程调试。 如果忘记了,远程调试会在 48 小时后自动关闭。

  1. 在发布配置文件页上,导航至“主机托管”部分。 选择省略号 (...),然后选择“ 在 Azure 门户中打开”。 Azure 门户将打开你的项目部署到的函数应用。

  2. 在函数应用中,选择 “设置>配置”,然后转到“ 常规设置 ”选项卡。在 远程调试旁边,选择“ 关闭”。 选择“ 保存”,然后选择“ 继续”。

函数应用重启后,你将不能再远程连接到远程进程。 可以在 Azure 门户中使用此相同选项卡在 Visual Studio 外部启用远程调试。

监控功能

监视函数的建议方法是将函数应用与 Application Insights 集成。 在 Visual Studio 发布期间创建函数应用时,应启用此集成。

如果在发布期间未设置集成,出于某种原因,仍应为 Azure 中的函数应用启用 Application Insights 集成

有关使用 Application Insights 进行监视的详细信息,请参阅 Azure Functions 中的监视执行

测试函数

本部分介绍如何创建一个 C# 进程内模型项目,可以使用适用于 .NET 的开源单元测试工具 xUnit 进行测试。

步骤 1:安装

按照以下步骤配置支持测试所需的环境,包括应用项目和函数:

  1. 在 Visual Studio 中,创建名为 Functions 的 Azure Functions 项目。

  2. 从模板创建 HTTP 函数:

    1. 解决方案资源管理器中,右键单击 Functions 项目,然后选择“ 添加新>的 Azure 函数”。
    2. 在“ 添加新项 ”对话框中,选择 “Azure 函数”,然后选择“ 添加”。
    3. 选择 Http 触发器,然后选择“ 添加”。
    4. 重命名新类 MyHttpTrigger
  3. 从模板创建计时器函数:

    1. 解决方案资源管理器中,右键单击 Functions 项目,然后选择“ 添加新>的 Azure 函数”。
    2. 在“ 添加新项 ”对话框中,选择 “Azure 函数”,然后选择“ 添加”。
    3. 选择 计时器触发器,然后选择“ 添加”。
    4. 重命名新类 MyTimerTrigger
  4. 在解决方案中创建 xUnit 测试应用

    1. 解决方案资源管理器中,右键单击包含 Functions 项目的解决方案,然后选择“ 添加新>项目”。
    2. 选择 xUnit 测试项目 模板,然后选择“ 下一步”。
    3. 将项目 命名为 Functions.Tests
  5. Functions.Tests 项目中删除默认测试文件。

  6. 使用 NuGet 将测试应用中的引用添加到 Microsoft.AspNetCore.Mvc。 可以使用包管理器控制台,也可以执行以下步骤:

    1. 解决方案资源管理器中,右键单击 Functions.Tests 项目,然后选择“ 管理 NuGet 包”。
    2. 搜索并安装 Microsoft.AspNetCore.Mvc
  7. Functions.Tests 应用中,添加对 Functions 应用的引用

    1. 解决方案资源管理器中,右键单击 Functions.Tests 项目,然后选择 “添加>项目引用”。
    2. 选择 Functions 项目,然后选择“ 确定”。

步骤 2:创建测试类

在本部分中,将创建用于运行自动测试的类。

每个函数都使用 ILogger 的实现来处理消息日志记录。 在某些测试中,不会记录任何消息,也不考虑如何实现日志记录。 其他测试需要评估记录的消息,以确定测试是否应通过。

  1. Functions.Tests 项目中创建一 NullScope 个名为并添加以下代码的类。 此类提供一个模拟范围。 在后面的步骤中,你将创建一个 ILogger 的实现,该实现使用此范围。

    using System;
    
    namespace Functions.Tests
    {
        public class NullScope : IDisposable
        {
            public static NullScope Instance { get; } = new NullScope();
    
            private NullScope() { }
    
            public void Dispose() { }
        }
    }
    
  2. Functions.Tests 项目中创建一 ListLogger 个名为并添加以下代码的类。 此类会维护在测试期间要评估的消息的内部列表。 为了实现所需的 ILogger 接口,该类使用类中的 NullScope 模拟范围。 测试用例将模拟范围传递给 ListLogger 类。

    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 接口协定的以下成员:

    • BeginScope:范围将上下文添加到日志记录。 在这种情况下,测试指向类上的 NullScope 静态实例,以允许测试正常运行。
    • IsEnabled:提供默认值 false
    • Log:此方法使用提供的 formatter 函数设置消息的格式。 然后,该方法将生成的文本添加到 Logs 集合中。

    Logs 集合是 List<string> 的实例,在构造函数中初始化。

  3. 在名为 LoggerTypes.csFunctions.Tests 项目中创建代码文件,并添加以下代码:

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

    此枚举指定测试使用的记录器类型。

  4. Functions.Tests 项目中创建名为 TestFactory 并添加以下代码的类:

    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", "Bernardo" },
                    new object[] { "name", "Ananya" },
                    new object[] { "name", "Vlad" }
    
                };
            }
    
            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;
            }
        }
    }
    

    TestFactory 类实现以下成员:

    • Data:此属性返回示例数据的 IEnumerable 集合。 键/值对表示传入查询字符串中的值。
    • CreateDictionary:此方法接受键值对作为参数。 它返回了Dictionary的新实例,该实例用于创建QueryCollection实例,后者用于表示查询字符串值。
    • CreateHttpRequest:此方法创建使用给定查询字符串参数初始化的 HTTP 请求。
    • CreateLogger:此方法返回用于测试的 ILogger 实现。 实现 ILogger 取决于指定的记录器类型。 如果指定了列表类型,则 ListLogger 实例将跟踪可用于在测试中评估的已记录消息。
  5. Functions.Tests 项目中创建名为 FunctionsTests 并添加以下代码的类:

    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", "Bernardo");
                var response = (OkObjectResult)await MyHttpTrigger.Run(request, logger);
                Assert.Equal("Hello, Bernardo. 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);
                new MyTimerTrigger().Run(null, logger);
                var msg = logger.Logs[0];
                Assert.Contains("C# Timer trigger function executed at", msg);
            }
        }
    }
    

    此类实现以下成员:

    • Http_trigger_should_return_known_string:此测试使用查询字符串值 name=Bernardo 创建对 HTTP 函数的请求。 此测试检查是否返回了预期的响应。
    • Http_trigger_should_return_string_from_member_data:此测试使用 xUnit 属性向 HTTP 函数提供示例数据。
    • Timer_should_log_message:此测试创建一个实例 ListLogger 并将其传递给计时器函数。 运行函数后,会检查日志以确保存在预期的消息。
  6. 若要访问测试中的应用程序设置,可以将具有模拟环境变量值的实现注入IConfiguration到函数中。

步骤 3:运行测试

若要在 Visual Studio 中运行测试,请选择 “查看>测试资源管理器”。 在测试资源管理器中,选择“运行>视图中的所有测试”。

Visual Studio 的屏幕截图。测试资源管理器显示已通过五个测试。在解决方案资源管理器中,Functions 和 Functions.Test 项目可见。

步骤 4:调试测试

若要调试测试,请对测试设置断点。 在 测试资源管理器中,选择“ 运行>调试上次运行”。