以编程方式在本地启动隔离的Azure功能(RabbitMQ触发),以进行集成测试

8fsztsew  于 2022-11-23  发布在  RabbitMQ
关注(0)|答案(1)|浏览(137)

我无法在集成测试中以编程方式本地启动Azure功能。
我读到的第一篇文章是:
programmatically-starting-function-app-is-failing-without-descriptive-output
可是却根本帮不上忙。
这是我的职责:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using AdmGroup.Omega.Framework.Net.Mail;
using Newtonsoft.Json;

namespace AdmGroup.Omega.Services.Infrastructure.EmailFunction.Functions;

public class EmailService
{
    private readonly IMailMessageSender mailMessageSender;

    public EmailService(IMailMessageSender mailMessageSender)
    {
        this.mailMessageSender = mailMessageSender;
    }

    [Function("EmailService")]
    public async Task<IActionResult> Run
    (
        [RabbitMQTrigger(queueName: "EmailQueue", ConnectionStringSetting = "admgroup:omega:services:infraestructure:emailservice:rabbitmq")] MailMessage email)
    {
        var strEmail = JsonConvert.SerializeObject(email);
        Console.WriteLine(strEmail);
        await mailMessageSender.SendAsync(email);

        return new OkResult();
    }
}

这是Program.cs(我在其中配置依赖项注入):

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using AdmGroup.Omega.Framework.EmailServicing.Services;
using AdmGroup.Omega.Framework.Net.Mail;
using AdmGroup.Omega.Framework.Security;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Extensions.Configuration;
using Azure.Identity;

var config = InitConfiguration();
var clientId = config["AzureKeyVault:ClientId"];
var keyVaultUrl = config["AzureKeyVault:KeyVaultUrl"];

if (!string.IsNullOrEmpty(clientId) && !string.IsNullOrEmpty(keyVaultUrl))
{
    var tokenCredential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = clientId });

    var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(s => s.AddSingleton<SecretClient>(_ => new(new Uri(keyVaultUrl), tokenCredential)))
    .ConfigureServices(s => s.AddSingleton<AzureKeyVaultSecretManager>())
    .ConfigureServices(s => s.AddSingleton<IMailMessageSender, Office365GraphEmailService>())
    .Build();

    await host.RunAsync();
}

/// <summary>
/// Allow test project to use an appsettings.json file.
/// Read RabbitMQ host and queue name from test appsettings
/// </summary>
/// <returns>Configuration</returns>
static IConfiguration InitConfiguration()
{
    return new ConfigurationBuilder()
       .AddJsonFile("local.settings.json")
        .AddEnvironmentVariables()
        .Build();
}

public partial class Program { }

如果我逃跑

func host start --dotnet-isolated-debug

从PowerShell(之前安装的"Azure Functions Core Tools")启动函数时没有任何问题,然后我可以运行测试,但问题是我需要从测试本身启动函数,这就是测试自动化的理念。
我可以在测试中注入AZ函数类,并临时删除函数参数,以便测试我能够启动它

emailService.Run()

并且它可以工作,我也可以调试,但问题是当参数再次启用时,它会抱怨Run()中缺少"MailMessage email"参数,这是它将从RabbitMQ队列中读取的内容,所以我卡住了...
接下来我可以尝试什么?

编辑1

最后我意识到这很容易,我可以只做Program.main()从测试和它应该只是工作,但问题是这样做我得到下面的异常:

Exception has occurred: CLR/System.InvalidOperationException
An exception of type 'System.InvalidOperationException' occurred in System.Private.CoreLib.dll but was not handled in user code: 'The gRPC channel URI 'http://:50852' could not be parsed.'
stszievb

stszievb1#

我对像这样的集成测试的2分钱。
对于集成测试,我通常更喜欢使用最接近生产的设置。对于Azure功能,最接近的设置是使用Docker容器或部署到Azure。
与其将其作为测试代码的一部分,最好使用一个脚本将该部分外部化,该脚本使用ARM模板部署到Azure,或者运行一个构建的Docker容器(在大多数情况下这要容易得多),然后针对该容器运行测试。
虽然构建自己的主机应该工作,因为Azure功能基本上是这样的,最好我会说它的确定单元测试,如果你需要测试的东西与依赖注入,例如,但对于集成测试,它不够接近生产对我的偏好。

相关问题