I'm not being able to launch an Azure function locally programmatically inside an integration test.
The first I've read was:
programmatically-starting-function-app-is-failing-without-descriptive-output
But was of not help.
This is my function:
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();
}
}
This is the Program.cs (where I configure dependency injection):
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 { }
If I run
func host start --dotnet-isolated-debug
from a PowerShell ("Azure Functions Core Tools" previously installed) the function just starts with no problem and I can then run the test, but the problem is that I need to launch the func from the test itself, that's the idea of test automation.
I can inject the AZ function class in the test and removing temporarily the function parameter for testing I was able to launch it with
emailService.Run()
and it works, I can also debug, but the problem is when the parameter is enabled again it complains about the lack of "MailMessage email" parameter in Run() that is what it will read from the RabbitMQ queue, so I'm stuck...
What can I try next?
Edit 1
I realized that in the end it's easy, I can just do Program.Main() from the test and it should just work, but the problem is doing that I get the following exception:
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.'