Azure SQL Trigger, Input, and Output binding support for the Azure Functions Test Framework.
dotnet add package AzureFunctions.TestFramework.Sql| Binding | Attribute | Description |
|---|---|---|
[SqlTrigger] |
Trigger | Receives a batch of row changes from SQL change tracking |
[SqlInput] |
Input | Reads rows from a SQL table or view via a query |
[SqlOutput] |
Output | Upserts rows into a SQL table — captured via FunctionInvocationResult |
Use InvokeSqlAsync to simulate a SQL change-tracking trigger with a batch of row changes.
using Microsoft.Azure.Functions.Worker.Extensions.Sql;
var changes = new[]
{
new SqlChange<Product>(SqlChangeOperation.Insert, new Product { Id = 1, Name = "Widget" }),
new SqlChange<Product>(SqlChangeOperation.Update, new Product { Id = 2, Name = "Gadget" })
};
var result = await host.InvokeSqlAsync("ProcessSqlChanges", changes);
Assert.True(result.Success);var json = """[{"operation":0,"item":{"id":1,"name":"Widget"}}]""";
var result = await host.InvokeSqlAsync("ProcessSqlChanges", json);
Assert.True(result.Success);Register fake rows via the builder so they are injected automatically for every invocation:
var host = await new FunctionsTestHostBuilder()
.WithFunctionsAssembly(typeof(MyFunction).Assembly)
.WithHostBuilderFactory(Program.CreateHostBuilder)
// Inject rows for [SqlInput(commandText: "SELECT * FROM Products", commandType: CommandType.Text, ...)]
.WithSqlInputRows("SELECT * FROM Products",
new List<Product>
{
new Product { Id = 1, Name = "Widget" },
new Product { Id = 2, Name = "Gadget" }
})
.BuildAndStartAsync();Use WithSqlInputRows<T>(commandText, T row) to inject a single row,
WithSqlInputRows<T>(commandText, IReadOnlyList<T> rows) to inject a list,
and WithSqlInputJson(commandText, json) to inject raw JSON.
The commandText must exactly match the value declared in the [SqlInput] attribute.
[Function("ReadSqlProducts")]
public void Run(
[QueueTrigger("products-queue")] string queueMessage,
[SqlInput(commandText: "SELECT * FROM Products",
commandType: System.Data.CommandType.Text,
connectionStringSetting: "SqlConnection")] IEnumerable<Product> products)
{
foreach (var product in products)
_logger.LogInformation("Product: {Name}", product.Name);
}Output bindings are captured automatically via FunctionInvocationResult:
var changes = new[] { new SqlChange<Product>(SqlChangeOperation.Insert, new Product { Id = 1, Name = "Widget" }) };
var result = await host.InvokeSqlAsync("UpsertSqlProduct", changes);
Assert.True(result.Success);
var written = result.ReadReturnValueAs<Product>();
Assert.Equal(1, written?.Id);[Function("UpsertSqlProduct")]
[SqlOutput(tableName: "Products", connectionStringSetting: "SqlConnection")]
public Product? Run(
[SqlTrigger(tableName: "Changes", connectionStringSetting: "SqlConnection")]
IReadOnlyList<SqlChange<Product>> changes)
{
return changes.FirstOrDefault()?.Item;
}Add the SQL package reference to your test project and all four function-app test flavours:
<PackageReference Include="AzureFunctions.TestFramework.Sql" />See the 4-flavour matrix test pattern for the concrete test class structure.