AzureFunctions.TestFramework.Core
0.11.2
dotnet add package AzureFunctions.TestFramework.Core --version 0.11.2
NuGet\Install-Package AzureFunctions.TestFramework.Core -Version 0.11.2
<PackageReference Include="AzureFunctions.TestFramework.Core" Version="0.11.2" />
<PackageVersion Include="AzureFunctions.TestFramework.Core" Version="0.11.2" />
<PackageReference Include="AzureFunctions.TestFramework.Core" />
paket add AzureFunctions.TestFramework.Core --version 0.11.2
#r "nuget: AzureFunctions.TestFramework.Core, 0.11.2"
#:package AzureFunctions.TestFramework.Core@0.11.2
#addin nuget:?package=AzureFunctions.TestFramework.Core&version=0.11.2
#tool nuget:?package=AzureFunctions.TestFramework.Core&version=0.11.2
AzureFunctions.TestFramework.Core
Core package for the Azure Functions Test Framework. Provides a TestServer/WebApplicationFactory-like in-process test host (FunctionsTestHost) for dotnet-isolated Azure Functions workers — no func.exe, no external processes, no open TCP ports.
Under the hood the framework uses ASP.NET Core's TestServer for both the gRPC communication channel and the worker's HTTP server.
Project setup
ASP.NET Core shared framework reference
If your function app uses ConfigureFunctionsWebApplication() (i.e., it references Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore), it must declare a framework reference to Microsoft.AspNetCore.App:
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
AzureFunctions.TestFramework.Core already declares this framework reference so that ASP.NET Core types are always resolved from the shared runtime in both the function app and the test framework. Without consistent framework resolution, HttpContextConverter cannot read HttpRequest from FunctionContext — the as HttpContext cast silently returns null due to a type identity mismatch between two physical copies of ASP.NET Core assemblies.
ℹ️ You do not need to add
FrameworkReferenceto your test project manually; it is propagated through the NuGet package metadata.
Usage
FunctionsTestHost — direct gRPC mode
Uses a custom gRPC host that mimics the Azure Functions host, starting the worker in-process and dispatching requests directly via the gRPC InvocationRequest channel. Works with ConfigureFunctionsWorkerDefaults().
Option A — Inline service registration (override individual services for test doubles):
_testHost = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyFunctions).Assembly)
.ConfigureServices(services =>
{
services.AddSingleton<IMyService, MockMyService>();
})
.BuildAndStartAsync();
Option B — IHostBuilder / Program.CreateWorkerHostBuilder (inherit all app services automatically):
// Program.cs — expose a worker-specific builder for gRPC mode testing
public partial class Program
{
public static IHostBuilder CreateWorkerHostBuilder(string[] args) =>
new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(ConfigureServices);
private static void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMyService, MyService>();
}
}
// Test — no need to re-register app services
_testHost = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyFunctions).Assembly)
.WithHostBuilderFactory(Program.CreateWorkerHostBuilder)
.BuildAndStartAsync();
Option C — FunctionsApplicationBuilder / Program.CreateApplicationBuilder (modern FunctionsApplication.CreateBuilder() startup style):
// Program.cs
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting; // required for UseMiddleware<T>()
public partial class Program
{
public static FunctionsApplicationBuilder CreateApplicationBuilder(string[] args)
{
var builder = FunctionsApplication.CreateBuilder(args);
builder.UseMiddleware<CorrelationMiddleware>();
builder.Services.AddSingleton<IMyService, MyService>();
return builder;
}
}
// Test
_testHost = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyFunctions).Assembly)
.WithHostApplicationBuilderFactory(Program.CreateApplicationBuilder)
.BuildAndStartAsync();
⚠️
UseMiddleware<T>()is an extension method fromMiddlewareWorkerApplicationBuilderExtensionsin theMicrosoft.Extensions.Hostingnamespace. Addusing Microsoft.Extensions.Hosting;to any file that calls it on aFunctionsApplicationBuilder.
FunctionsTestHost — ASP.NET Core integration mode
The same FunctionsTestHost automatically detects when the worker uses ConfigureFunctionsWebApplication() and routes HttpClient requests to the worker's in-memory TestServer instead of dispatching over gRPC. The full ASP.NET Core middleware pipeline runs, and all ASP.NET Core-native binding types (HttpRequest, FunctionContext, typed route params, CancellationToken) work correctly. No TCP port is opened — the worker's Kestrel IServer is replaced with TestServer at startup.
IHostBuilder style:
// Program.cs
public partial class Program
{
public static IHostBuilder CreateHostBuilder(string[] args) =>
new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices(ConfigureServices);
}
// Test — framework auto-detects ASP.NET Core mode and routes requests to in-memory TestServer
_testHost = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyFunctions).Assembly)
.WithHostBuilderFactory(Program.CreateHostBuilder)
.BuildAndStartAsync();
FunctionsApplicationBuilder style:
// Program.cs
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
public partial class Program
{
public static FunctionsApplicationBuilder CreateWebApplicationBuilder(string[] args)
{
var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication(); // call before UseMiddleware
builder.UseMiddleware<CorrelationMiddleware>();
builder.Services.AddSingleton<IMyService, MyService>();
return builder;
}
}
// Test
_testHost = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyFunctions).Assembly)
.WithHostApplicationBuilderFactory(Program.CreateWebApplicationBuilder)
.BuildAndStartAsync();
ℹ️ The framework auto-detects which mode is in use. With
ConfigureFunctionsWorkerDefaults(), HTTP requests are dispatched via the gRPCInvocationRequestchannel. WithConfigureFunctionsWebApplication(), the framework replaces the worker's KestrelIServerwith an in-memoryTestServer— no TCP port is opened.
Service overrides — ConfigureServices on FunctionsTestHostBuilder lets tests swap out any registered service regardless of mode:
_testHost = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyFunctions).Assembly)
.WithHostBuilderFactory(Program.CreateHostBuilder)
.ConfigureServices(services =>
{
services.RemoveAll<IMyService>();
services.AddSingleton<IMyService>(new MockMyService());
})
.BuildAndStartAsync();
Service access + configuration overrides — FunctionsTestHost.Services exposes the worker DI container after startup, and FunctionsTestHostBuilder.ConfigureSetting("Demo:Message", "configured-value") lets tests overlay configuration values.
Optional shared-host pattern — if a test class can safely reset mutable app state between tests, it can amortize worker startup with an IClassFixture. See the sample projects for a concrete example.
Worker-side Logging
By default the framework suppresses the worker host's logging below Warning. To see ILogger output from inside function code in test results, use ConfigureWorkerLogging:
xUnit:
public async ValueTask InitializeAsync()
{
var loggerProvider = new XUnitLoggerProvider(_output);
_host = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyFunctions).Assembly)
.WithLoggerFactory(LoggerFactory.Create(b => b.AddProvider(loggerProvider)))
.ConfigureWorkerLogging(logging =>
{
logging.SetMinimumLevel(LogLevel.Information);
logging.AddProvider(loggerProvider);
})
.BuildAndStartAsync();
}
NUnit:
[SetUp]
public async Task SetUp()
{
var loggerProvider = new NUnitLoggerProvider();
_host = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyFunctions).Assembly)
.WithLoggerFactory(LoggerFactory.Create(b => b.AddProvider(loggerProvider)))
.ConfigureWorkerLogging(logging =>
{
logging.SetMinimumLevel(LogLevel.Information);
logging.AddProvider(loggerProvider);
})
.BuildAndStartAsync();
}
Key points:
WithLoggerFactorycontrols framework infrastructure logs (gRPC, host lifecycle, etc.)ConfigureWorkerLoggingcontrols the worker host's logging pipeline — this is where function-sideILoggercalls flow- Category-specific filters override the global minimum:
logging.AddFilter("MyApp.Functions", LogLevel.Debug) - The same
ILoggerProviderinstance can be shared between both
Architecture & Design Decisions
Assembly Load Context (ALC) isolation prevention
Problem: The Worker SDK's DefaultMethodInfoLocator.GetMethod() calls AssemblyLoadContext.Default.LoadFromAssemblyPath() during FunctionLoadRequest processing. When the worker runs in-process (same process as the test runner), this can load a second copy of the same assembly, creating two distinct RuntimeTypeHandle values for the same type name. The SDK's built-in converters use context.TargetType == typeof(T) checks that silently fail, leaving trigger parameters null.
Solution (three layers):
- Root fix —
InProcessMethodInfoLocator: Replaces the SDK's internalIMethodInfoLocatorviaDispatchProxy. SearchesAppDomain.CurrentDomain.GetAssemblies()for already-loaded assemblies instead of callingLoadFromAssemblyPath. Registered withAddSingleton(notTryAdd) so it wins over the SDK'sTryAddSingleton. - Defense-in-depth —
TestFunctionContextConverter+TestHttpRequestConverter: Registered at converter index 0. These compare types byFullNamestrings (immune to dual-load) and use reflection to access properties (bypassingis Tcasts). - Build-time —
<FrameworkReference Include="Microsoft.AspNetCore.App" />: Declared in the Core csproj so ASP.NET Core types always resolve from the shared runtime.
Function ID resolution
GeneratedFunctionMetadataProvider computes a stable hash for each function (Name + ScriptFile + EntryPoint). GrpcHostService stores the hash-based FunctionId from FunctionMetadataResponse — not the GUID from FunctionLoadRequest — because the worker's internal _functionMap uses the hash.
GrpcWorker.StopAsync() is a no-op
The Azure Functions worker SDK's GrpcWorker.StopAsync() returns Task.CompletedTask immediately — it does not close the gRPC channel. FunctionsTestHost calls _grpcHostService.SignalShutdownAsync() before stopping _grpcServerManager to gracefully end the EventStream so TestServer can stop instantly.
Durable converter interception
When using ConfigureFunctionsWebApplication(), the ASP.NET Core path does not send synthetic durable binding data in InputData. The real DurableTaskClientConverter receives null/empty context.Source and [ConverterFallbackBehavior(Disallow)] blocks fallback. The framework fixes this by registering FakeDurableTaskClientInputConverter in DI as the service for the real DurableTaskClientConverter type, so ActivatorUtilities.GetServiceOrCreateInstance returns the fake converter instead.
IAutoConfigureStartup scanning
The functions assembly contains source-generated classes (FunctionMetadataProviderAutoStartup, FunctionExecutorAutoStartup) implementing IAutoConfigureStartup. WorkerHostService scans for and invokes these to register GeneratedFunctionMetadataProvider and DirectFunctionExecutor, overriding the defaults that would require a functions.metadata file on disk.
Binding cache clearing (FunctionsApplicationBuilder + ASP.NET Core)
Problem: The Worker SDK's IBindingCache<ConversionResult> uses binding-name-only keys. If user middleware calls GetHttpRequestDataAsync() before FunctionExecutionMiddleware runs, the cache stores a GrpcHttpRequestData under key "req". Later, FunctionExecutionMiddleware calls BindFunctionInputAsync for the same "req" binding with TargetType=HttpRequest — the cache returns the stale GrpcHttpRequestData and the SDK casts (HttpRequest)GrpcHttpRequestData → InvalidCastException.
Solution: In the FunctionsApplicationBuilder path, WorkerHostService appends a cache-clearing middleware that checks context.Items.ContainsKey("HttpRequestContext") — confirming ASP.NET Core mode is active — and uses BindingCacheCleaner.TryClearBindingCache(context.InstanceServices) to clear the internal ConcurrentDictionary via reflection. Converters then re-run with correct state.
ℹ️ In the
IHostBuilderpath, the cache-poisoning scenario is avoided as long asConfigureFunctionsWebApplication()is called before anyUseMiddleware<T>()calls.
Custom route prefix auto-detection
FunctionsTestHostBuilder.Build() reads extensions.http.routePrefix from the functions assembly's host.json. The prefix is used by FunctionsHttpMessageHandler (to strip it when matching routes) and by FunctionsTestHost (to set HttpClient.BaseAddress). This makes custom route prefixes work transparently without test-side configuration.
References
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Grpc.AspNetCore (>= 2.76.0)
- Microsoft.AspNetCore.TestHost (>= 10.0.5)
- Microsoft.Azure.Functions.Worker (>= 2.51.0)
- Microsoft.Azure.Functions.Worker.Grpc (>= 2.51.0)
-
net8.0
- Grpc.AspNetCore (>= 2.76.0)
- Microsoft.AspNetCore.TestHost (>= 8.0.25)
- Microsoft.Azure.Functions.Worker (>= 2.51.0)
- Microsoft.Azure.Functions.Worker.Grpc (>= 2.51.0)
NuGet packages (10)
Showing the top 5 NuGet packages that depend on AzureFunctions.TestFramework.Core:
| Package | Downloads |
|---|---|
|
AzureFunctions.TestFramework.Timer
TimerTrigger invocation support for the Azure Functions Test Framework. Enables triggering timer functions in integration tests. |
|
|
AzureFunctions.TestFramework.Http
HTTP test client support for AzureFunctions.TestFramework |
|
|
AzureFunctions.TestFramework.Blob
BlobTrigger invocation support for the Azure Functions Test Framework. Enables triggering blob-triggered functions in integration tests. |
|
|
AzureFunctions.TestFramework.ServiceBus
ServiceBusTrigger invocation support for the Azure Functions Test Framework. Enables triggering Service Bus-triggered functions in integration tests. |
|
|
AzureFunctions.TestFramework.EventGrid
EventGridTrigger invocation support for the Azure Functions Test Framework. Enables triggering Event Grid-triggered functions in integration tests. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.11.2 | 180 | 4/14/2026 |
| 0.11.1 | 187 | 4/13/2026 |
| 0.11.0 | 202 | 4/13/2026 |
| 0.10.0 | 222 | 4/11/2026 |
| 0.9.0 | 199 | 4/8/2026 |
| 0.8.0 | 180 | 4/7/2026 |
| 0.7.1 | 187 | 4/7/2026 |
| 0.7.0 | 181 | 4/7/2026 |
| 0.6.0 | 198 | 4/7/2026 |
| 0.5.0 | 189 | 4/2/2026 |
| 0.4.1 | 207 | 4/1/2026 |
| 0.4.0 | 199 | 4/1/2026 |
| 0.3.0 | 202 | 4/1/2026 |
| 0.2.1 | 207 | 3/17/2026 |
| 0.2.0 | 199 | 3/17/2026 |