Fake-backed Durable Functions test helpers for the Azure Functions Test Framework. Includes ConfigureFakeDurableSupport(...), FakeDurableTaskClient, direct activity invocation, fake orchestration scheduling, sub-orchestrators, external events, and HTTP status polling helpers.
No real Durable Task runtime or external storage (Azure Storage / Netherite / MSSQL) is needed.
The real Durable Task execution engine relies on external storage and the Durable Task Framework host, neither of which runs inside the test framework's in-process worker. ConfigureFakeDurableSupport(...) registers a FakeDurableTaskClient and companion types that intercept [DurableClient] binding resolution at the DI level, letting starter functions, orchestrators, activities, and sub-orchestrators execute fully in-process.
using AzureFunctions.TestFramework.Core;
using AzureFunctions.TestFramework.Durable;
_testHost = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyDurableFunction).Assembly)
.ConfigureFakeDurableSupport(provider =>
{
// Register orchestration runners
provider.AddOrchestration<string>("MyOrchestrator", ctx => MyOrchestratorFunction.RunAsync(ctx));
})
.BuildAndStartAsync();[DurableClient] DurableTaskClientinjection (both direct gRPC and ASP.NET Core integration paths)- Direct activity invocation via
IFunctionsTestHost.InvokeActivityAsync<TResult>(...) - Fake orchestration scheduling and activity execution
- Sub-orchestrator execution via
TaskOrchestrationContext.CallSubOrchestratorAsync<TResult>() - Custom status via
SetCustomStatus(...)andOrchestrationMetadata.ReadCustomStatusAs<T>() - External events: both wait-then-raise and buffered raise-before-wait flows
FunctionsDurableClientProviderresolution fromFunctionsTestHost.Services
public class DurableFunctionTests : IAsyncLifetime
{
private IFunctionsTestHost _testHost;
public async Task InitializeAsync()
{
_testHost = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(OrderOrchestrator).Assembly)
.WithHostBuilderFactory(Program.CreateHostBuilder)
.ConfigureFakeDurableSupport(provider =>
{
provider.AddOrchestration<OrderResult>("OrderOrchestrator",
ctx => OrderOrchestratorFunction.RunAsync(ctx));
provider.AddActivity<string, OrderResult>("ProcessOrder",
input => new OrderResult { Success = true, OrderId = input });
})
.BuildAndStartAsync();
}
[Fact]
public async Task StartOrder_StartsOrchestration_AndCompletes()
{
// Start via HTTP starter function
using var client = _testHost.CreateHttpClient();
var response = await client.PostAsync("/api/orders/start", JsonContent.Create(new { orderId = "123" }));
response.EnsureSuccessStatusCode();
var instanceId = await response.Content.ReadAsStringAsync();
// Resolve the durable client and check status
var durableClient = _testHost.Services
.GetRequiredService<FunctionsDurableClientProvider>()
.GetClient();
var metadata = await durableClient.GetInstanceAsync(instanceId);
Assert.Equal(OrchestrationRuntimeStatus.Completed, metadata!.RuntimeStatus);
}
[Fact]
public async Task ProcessOrder_Activity_ReturnsResult()
{
// Invoke activity directly
var result = await _testHost.InvokeActivityAsync<OrderResult>("ProcessOrder", "order-123");
Assert.True(result.Success);
Assert.Equal("order-123", result.OrderId);
}
[Fact]
public async Task OrderOrchestrator_WithExternalEvent_Completes()
{
var durableClient = _testHost.Services
.GetRequiredService<FunctionsDurableClientProvider>()
.GetClient() as FakeDurableTaskClient;
var instanceId = await durableClient!.ScheduleNewOrchestrationInstanceAsync("OrderOrchestrator", "order-123");
// Raise external event to unblock orchestrator
await durableClient.RaiseEventAsync(instanceId, "ApprovalReceived", true);
var metadata = await durableClient.GetInstanceAsync(instanceId);
Assert.Equal(OrchestrationRuntimeStatus.Completed, metadata!.RuntimeStatus);
}
public async Task DisposeAsync()
{
await _testHost.StopAsync();
_testHost.Dispose();
}
}MIT