Visual Studio 提供了一种开发、测试和部署 C# 类库函数的方法。 如果此体验是你第一次使用 Azure Functions,请参阅 Azure Functions 概述。
若要立即开始,请考虑完成 Visual Studio 的函数快速入门。
本文详细介绍了如何使用 Visual Studio 开发 C# 类库函数并将其发布到 Azure。 有两个模型用于开发 C# 类库函数: 独立辅助角色模型 和 进程内模型。
你正在阅读此文章的独立工作者模型版本。 可以在文章顶部选择首选模型。
您正在阅读本文章的正在处理的模型版本。 可以在文章顶部选择首选模型。
除非另有说明,否则将演示 Visual Studio 2022 的过程和示例。 有关 Visual Studio 2022 版本的详细信息,请参阅 发行说明 或 预览发行说明。
先决条件
Visual Studio 2022,包括“Azure 开发”工作负载。
所需的其他资源(例如 Azure 存储帐户)将在发布过程中在订阅中创建。
如果没有 Azure 订阅,可在开始前创建一个试用帐户。
创建 Azure Functions 项目
Visual Studio 中的 Azure Functions 项目模板创建了一个 C# 类库项目,该项目可发布到 Azure 中的函数应用。 可使用函数应用将函数分组为逻辑单元,以便更轻松地管理、部署、缩放和共享资源。
在 Visual Studio 菜单中,选择“文件”“新建”>“项目”。
在“ 创建新项目 ”对话框中,在搜索框中输入 函数 ,选择 Azure Functions 模板,然后选择“ 下一步”。
在 “配置新项目 ”对话框中,对于 “项目名称”,输入项目的名称,然后选择“ 下一步”。 函数应用名称必须可以充当 C# 命名空间,因此请勿使用下划线、连字符或任何其他的非字母数字字符。
在“ 其他信息 ”对话框中,执行下表中列出的作:
设置 Action 说明 Functions 辅助角色 选择.NET 8.0 独立(长期支持)。 Visual Studio 创建一个在 独立工作进程中运行的函数项目。 隔离的工作进程还支持其他不提供长期支持(LTS)的 .NET 和 .NET Framework 版本。 有关详细信息,请参阅 Azure Functions 运行时版本概述。 功能 选择 Http 触发器。 Visual Studio 创建由 HTTP 请求触发的函数。 将 Azurite 用于运行时存储账户 (AzureWebJobsStorage) 选中此复选框。 由于 Azure 中的函数应用需要存储帐户,因此在将项目发布到 Azure 时会分配或创建一个存储帐户。 HTTP 触发器不使用存储帐户连接字符串。 所有其他触发器类型都需要有效的存储帐户连接字符串。 授权级别 选择 “匿名”。 使用此授权设置时,任何客户端都可以触发创建的函数,而无需提供密钥。 通过此配置,可以轻松测试新函数。 有关详细信息,请参阅授权级别。 设置 Action 说明 Functions 辅助角色 选择 .NET 8.0 进程内托管(长期支持)。 Visual Studio 创建一个函数项目,该项目使用 4.x 版 Functions 运行时运行进程内。 有关详细信息,请参阅 Azure Functions 运行时版本概述。 功能 选择 Http 触发器。 Visual Studio 创建由 HTTP 请求触发的函数。 将 Azurite 用于运行时存储账户 (AzureWebJobsStorage) 选中此复选框。 由于 Azure 中的函数应用需要存储帐户,因此在将项目发布到 Azure 时会分配或创建一个存储帐户。 HTTP 触发器不使用存储帐户连接字符串。 所有其他触发器类型都需要有效的存储帐户连接字符串。 授权级别 选择 匿名 使用此授权设置时,任何客户端都可以触发创建的函数,而无需提供密钥。 通过此配置,可以轻松测试新函数。 有关详细信息,请参阅授权级别。 请确保将“授权级别”设置为“匿名”。 如果选择函数的默认 级别,则需要在请求中显示 函数密钥 才能访问函数终结点。
选择“创建”以创建函数项目和 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 可能包含机密(如连接字符串),因此你绝不应将其存储在远程存储库中。 若要了解有关本地设置的详细信息,请参阅本地设置文件。
发布项目时,Visual Studio 不会自动上传 local.settings.json 中的设置。 为了确保这些设置也存在于 Azure 的函数应用中,请在发布项目之后上传它们。 有关详细信息,请参阅函数应用设置。 集合中的 ConnectionStrings
值不会发布。
还可以在代码中将函数应用设置值读取为环境变量。 有关详细信息,请参阅环境变量。
为本地开发配置项目
Functions 运行时在内部使用存储帐户。 在开发过程中,可以使用此内部帐户的有效存储帐户,也可以使用 Azurite 模拟器。
对于 HTTP 和 Webhook 以外的所有触发器类型,需要在 Values.AzureWebJobsStorage
文件中设置密钥的值:
- 对于存储帐户,请将该值设置为存储帐户的连接字符串。
- 对于模拟器,请将值设置为
UseDevelopmentStorage=true
.
如果使用模拟器,请将此设置更改为部署前的实际存储帐户连接字符串。 有关详细信息,请参阅本地存储模拟器。
若要设置存储帐户连接字符串,请执行以下步骤:
登录到 Azure 门户,然后转到存储帐户。
选择 “安全性 + 网络>访问密钥”。 在 key1 下,复制 连接字符串 值。
在 Visual Studio 项目中,打开 local.settings.json 文件。 将
AzureWebJobsStorage
键的值设置为复制的连接字符串。重复上一步骤,将唯一键添加到函数所需的其他任何连接的
Values
数组。
将函数添加到项目
在 C# 类库函数中,函数使用的绑定是通过在代码中应用属性定义的。 从提供的模板创建函数触发器时,将为你应用触发器属性。
在“解决方案资源管理器”中,右键单击你的项目节点,然后选择“添加”“新建 Azure 函数”。>
在“ 添加新项 ”对话框中,选择 “Azure 函数”,然后选择“ 添加”。
选择触发器,然后设置所需的绑定属性。 如果选择存储服务触发器并想要配置连接,请选中用于配置触发器连接的复选框。 以下示例显示了用于创建队列存储触发器函数的设置。
选择 并添加。 如果在上一步中选择用于配置存储连接的复选框,将显示 “连接到依赖项 ”页。 选择 Azurite 存储模拟器或 Azure 存储,然后选择“ 下一步”。
- 如果选择 Azurite 存储模拟器,将显示 “连接到存储 Azurite 模拟器 ”页。 执行以下步骤:
- 选择“下一步”。
- 在 “更改摘要 ”页上,选择“ 完成”。 Visual Studio 配置依赖项并创建触发器类。
- 如果选择 Azure 存储,将显示 “连接到 Azure 存储 ”页。 执行以下步骤:
- 选择存储帐户,然后选择“ 下一步”。 Visual Studio 尝试连接到 Azure 帐户并检索终结点。
- 选择“下一步”。
- 在 “更改摘要 ”页上,选择“ 完成”。 Visual Studio 配置依赖项并创建触发器类。
此触发器示例使用应用程序设置与名为
QueueStorage
的密钥建立存储连接。 此密钥存储在 local.settings.json 文件中,引用 Azurite 模拟器或存储帐户。- 如果选择 Azurite 存储模拟器,将显示 “连接到存储 Azurite 模拟器 ”页。 执行以下步骤:
检查新添加的类。 例如,以下 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 触发器和绑定。
添加绑定
使用触发器时,输入和输出绑定是作为绑定属性添加到函数的。 若要向函数添加绑定,请执行以下步骤:
确保已为本地开发配置项目。
为每个特定绑定添加相应的 NuGet 扩展包。 有关特定于绑定的 NuGet 包要求,请参考关于绑定的文章。 例如,需要了解有关 Azure 事件中心触发器的软件包要求,请参阅 用于 Azure Functions 的 Azure 事件中心触发器和绑定。
在包管理器控制台中使用以下命令安装特定包:
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 上的单个包页上列出了有效版本。如果有绑定所需的应用设置,请将其添加到
Values
中的 集合。函数在本地运行时将使用这些值。 当函数在 Azure 的函数应用中运行时,它将使用函数应用设置。 使用 Visual Studio 可以轻松地将本地设置发布到 Azure。
将适当的绑定属性添加到方法签名。 在以下代码中,队列消息触发函数
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 中启动函数,请执行以下步骤:
选择 F5。 如果系统提示,请接受来自 Visual Studio 的请求以下载并安装 Azure Functions Core Tools。 可能还需要打开防火墙例外,以便工具可以处理 HTTP 请求。
项目运行时,测试代码的方式与测试已部署的函数的方式相同。
在调试模式下运行 Visual Studio 时,将会按预期命中断点。
有关使用 Visual Studio 的更详细的测试方案,请参阅本文后面的 测试函数。
发布到 Azure
将 Functions 项目发布到 Azure 时,Visual Studio 使用 zip 部署 来部署项目文件。 如果可能,还应选择 “从包文件运行 ”,以便项目在部署(.zip)包中运行。 有关详细信息,请参阅从 Azure 中的包文件运行函数。
不要使用 Web 部署 (msdeploy
) 部署到 Functions。
使用以下步骤将项目发布到 Azure 中的函数应用:
在“解决方案资源管理器” 中,右键单击该项目并选择“发布”。 在“目标”中,选择“Azure”,然后选择“下一步”。
对于“特定目标”,请选择“Azure 函数应用(Windows)”(这将创建一个在 Windows 上运行的函数应用),然后选择“下一步”。
在“函数实例”中,选择“创建新的 Azure 函数…”。
使用下表中指定的值创建新的实例:
设置 值 说明 名称 全局唯一名称 用于唯一标识新 Function App 的名称。 接受此名称或输入新名称。 有效的字符是 a-z
、0-9
和-
。订阅 你的订阅 要使用的 Azure 订阅。 接受此订阅,或从下拉列表中选择一个新订阅。 资源组 资源组的名称 你要在其中创建函数应用的资源组。 从下拉列表中选择一个现有资源组,或者选择“新建”来创建一个新的资源组。 计划类型 消耗 将项目发布到在消耗计划中运行的函数应用时,只需为函数应用的执行付费。 其他托管计划会产生更高的成本。 位置 应用服务的位置 在靠近你或者靠近函数访问的其他服务的区域中选择一个位置。 Azure 存储 常规用途存储帐户 Functions 运行时需要 Azure 存储帐户。 选择“新建”即可配置常规用途存储帐户。 也可选择一个符合存储帐户要求的现有帐户。 选择“创建”,在 Azure 中创建一个函数应用及其相关资源。 资源创建的状态将显示在窗口左下角。
在“函数实例”中,确保已选中“从包文件运行”。 启用从包运行模式,函数应用将使用 Zip 部署进行部署。 建议为你的函数项目使用 Zip 部署方法,因为它可提高性能。
选择“完成”,然后在“发布”页面上选择“发布”,将包含项目文件的包部署到 Azure 中的新函数应用 。
部署完成后,Azure 中函数应用的根 URL 将显示在“发布”选项卡中。
在“发布”选项卡中的“托管”部分中,选择“在 Microsoft Azure 门户中打开”。 此操作会在 Microsoft Azure 门户中打开新的函数应用 Azure 资源。
函数应用设置
发布项目时,Visual Studio 不会自动上传应用设置。 如果将设置添加到 local.settings.json 文件,还必须将它们添加到 Azure 中的函数应用。
将所需设置上传到 Azure 中的函数应用的最简单方法是在 Visual Studio 中管理这些设置。 在发布配置文件页上,导航至“主机托管”部分。 选择省略号(...),然后选择“ 管理 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 主机的进程中运行的函数应用,请执行以下步骤:
在发布配置文件页上,导航至“主机托管”部分。 选择省略号 (...),然后选择 “附加调试器”。
Visual Studio 连接到您的函数应用程序,并打开远程调试(如果尚未开启)。
注意
由于远程调试器无法连接到主机进程,因此可能会出现错误消息。 在任何情况下,本地调试器都无法访问断点,也无法提供检查变量或逐步执行代码的方法。
在 Visual Studio 调试 菜单上,选择“ 附加到进程”。
在“ 附加到进程 ”对话框中,执行以下步骤:
- 在 “连接类型”旁边,选择 “Azure 应用服务”。
- 在 “连接目标”旁边,选择“ 查找”。
在“Azure 附加到进程”对话框中,搜索你的函数应用并将其选中,然后选择“确定”。
如果系统提示,请允许通过本地防火墙访问 Visual Studio。
返回“ 附加到进程 ”对话框,为 所有用户选择“显示进程”。 选择 dotnet.exe,然后选择 “附加”。
操作完成后,会附加到在隔离工作进程中运行的 C# 类库代码。 此时,你可正常调试函数应用。
若要使用 Functions 主机将远程调试器附加到进程内运行的函数应用,请执行以下步骤。
在发布配置文件页上,导航至“主机托管”部分。 选择省略号 (...),然后选择 “附加调试器”。
Visual Studio 连接到您的函数应用程序,并打开远程调试(如果尚未开启)。 它还查找调试器并将其附加到应用的主机进程。 此时,你可正常调试函数应用。
完成调试后,应 关闭远程调试。
关闭远程调试
完成代码远程调试后,应在 Azure 门户中关闭远程调试。 如果忘记了,远程调试会在 48 小时后自动关闭。
在发布配置文件页上,导航至“主机托管”部分。 选择省略号 (...),然后选择“ 在 Azure 门户中打开”。 Azure 门户将打开你的项目部署到的函数应用。
在函数应用中,选择 “设置>配置”,然后转到“ 常规设置 ”选项卡。在 远程调试旁边,选择“ 关闭”。 选择“ 保存”,然后选择“ 继续”。
函数应用重启后,你将不能再远程连接到远程进程。 可以在 Azure 门户中使用此相同选项卡在 Visual Studio 外部启用远程调试。
监控功能
监视函数的建议方法是将函数应用与 Application Insights 集成。 在 Visual Studio 发布期间创建函数应用时,应启用此集成。
如果在发布期间未设置集成,出于某种原因,仍应为 Azure 中的函数应用启用 Application Insights 集成 。
有关使用 Application Insights 进行监视的详细信息,请参阅 Azure Functions 中的监视执行。
测试函数
本部分介绍如何创建一个 C# 进程内模型项目,可以使用适用于 .NET 的开源单元测试工具 xUnit 进行测试。
步骤 1:设置
按照以下步骤配置支持测试所需的环境,包括应用项目和函数:
在 Visual Studio 中,创建名为 Functions 的 Azure Functions 项目。
从模板创建 HTTP 函数:
- 在 解决方案资源管理器中,右键单击 Functions 项目,然后选择“ 添加新>的 Azure 函数”。
- 在“ 添加新项 ”对话框中,选择 “Azure 函数”,然后选择“ 添加”。
- 选择 Http 触发器,然后选择“ 添加”。
- 重命名新类 MyHttpTrigger。
从模板创建计时器函数:
- 在 解决方案资源管理器中,右键单击 Functions 项目,然后选择“ 添加新>的 Azure 函数”。
- 在“ 添加新项 ”对话框中,选择 “Azure 函数”,然后选择“ 添加”。
- 选择 计时器触发器,然后选择“ 添加”。
- 重命名新类 MyTimerTrigger。
在解决方案中创建 xUnit 测试应用 :
- 在 解决方案资源管理器中,右键单击包含 Functions 项目的解决方案,然后选择“ 添加新>项目”。
- 选择 xUnit 测试项目 模板,然后选择“ 下一步”。
- 将项目 命名为 Functions.Tests。
从 Functions.Tests 项目中删除默认测试文件。
使用 NuGet 将测试应用中的引用添加到 Microsoft.AspNetCore.Mvc。 可以使用包管理器控制台,也可以执行以下步骤:
- 在 解决方案资源管理器中,右键单击 Functions.Tests 项目,然后选择“ 管理 NuGet 包”。
- 搜索并安装 Microsoft.AspNetCore.Mvc。
在 Functions.Tests 应用中,添加对 Functions 应用的引用:
- 在 解决方案资源管理器中,右键单击 Functions.Tests 项目,然后选择 “添加>项目引用”。
- 选择 Functions 项目,然后选择“ 确定”。
步骤 2:创建测试类
在本部分中,将创建用于运行自动测试的类。
每个函数都使用 ILogger
的实现来处理消息日志记录。 在某些测试中,不会记录任何消息,也不考虑如何实现日志记录。 其他测试需要评估记录的消息,以确定测试是否应通过。
在 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() { } } }
在 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>
的实例,在构造函数中初始化。-
在名为 LoggerTypes.cs 的 Functions.Tests 项目中创建代码文件,并添加以下代码:
namespace Functions.Tests { public enum LoggerTypes { Null, List } }
此枚举指定测试使用的记录器类型。
在 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
实例将跟踪可用于在测试中评估的已记录消息。
-
在 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
并将其传递给计时器函数。 运行函数后,会检查日志以确保存在预期的消息。
-
若要访问测试中的应用程序设置,可以将具有模拟环境变量值的实现注入
IConfiguration
到函数中。
步骤 3:运行测试
若要在 Visual Studio 中运行测试,请选择 “查看>测试资源管理器”。 在测试资源管理器中,选择“运行>视图中的所有测试”。
步骤 4:调试测试
若要调试测试,请对测试设置断点。 在 测试资源管理器中,选择“ 运行>调试上次运行”。