Why is IConfiguration null when integration testing my Azure Functions?

The current version of the Microsoft.Azure.Functions.Extensions package exposes an additional property that allows you easy access to the IConfiguration provided to the function. Previously this required manually building a service provider, which was obviously problematic.

Using that package my FunctionsStartup.cs looks like this:

public override void Configure(IFunctionsHostBuilder builder)
{
    base.Configure(builder);

    var config = builder.GetContext().Configuration; // new in v1.1.0 of Microsoft.Azure.Functions.Extensions
    var mySetting = config["MySetting"];

    int.Parse(mySetting, out var mySetting);

    // ... use mySetting...
}

In order to test my HTTP-triggered functions I've used this article as a base, which details how to manually build and start a host to execute my function as if it was running in Azure, similar to how TestServer works in ASP.NET Core:

var host = new HostBuilder()
    .ConfigureWebJobs(new FunctionsStartup().Configure)
    .Build();

var functionsInstance = ActivatorUtilities.CreateInstance<MyFunctions>(host.Services);

I can then execute the function methods defined on MyFunctions to test their responses:

var request = new DefaultHttpRequest(new DefaultHttpContext());

var response = (OkObjectResult)functionsInstance.HttpTriggerMethod(request);

... assert that response is valid

The problem is that when I run my tests, builder.GetContext().Configuration is returning null in FunctionsStartup.Configure, which of course causes those tests to fail. How can I work around this?

Upvotes: 6

Views: 1188

Answers (1)

The article I linked to hasn't been updated to take into account the existence of builder.GetContext().Configuration, but you can make this work for testing purposes with a little tweaking. Instead of using:

var host = new HostBuilder()
    .ConfigureWebJobs(new FunctionsStartup().Configure)
    .Build();

you need to explicitly copy the host's settings into a new WebJobsBuilderContext that you then pass to your function's startup:

var host = new HostBuilder()
    .ConfigureWebJobs((context, builder) => new FunctionsStartup().Configure(new WebJobsBuilderContext
    {
        ApplicationRootPath = context.HostingEnvironment.ContentRootPath,
        Configuration = context.Configuration,
        EnvironmentName = context.HostingEnvironment.EnvironmentName,
    }, builder))
    .Build();

I'm not sure if this is the completely correct way to achieve this, but it has worked well for me.

Upvotes: 10

Related Questions