Tracon.Testing 1.0.0-preview.3

Prefix Reserved
This is a prerelease version of Tracon.Testing.
dotnet add package Tracon.Testing --version 1.0.0-preview.3
                    
NuGet\Install-Package Tracon.Testing -Version 1.0.0-preview.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Tracon.Testing" Version="1.0.0-preview.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tracon.Testing" Version="1.0.0-preview.3" />
                    
Directory.Packages.props
<PackageReference Include="Tracon.Testing" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Tracon.Testing --version 1.0.0-preview.3
                    
#r "nuget: Tracon.Testing, 1.0.0-preview.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Tracon.Testing@1.0.0-preview.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Tracon.Testing&version=1.0.0-preview.3&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Tracon.Testing&version=1.0.0-preview.3&prerelease
                    
Install as a Cake Tool

Tracon.Testing

Helpers that let a consumer building an agent on Tracon test their own agent without calling a real model: a non-networked model provider, an in-memory host fixture, and assertions on run records.

The package binds to no test framework (xunit, NUnit, MSTest, Shouldly, FluentAssertions). It throws TraconAssertionException when an assertion fails; every framework counts that as a failure.

Setup

dotnet add package Tracon.Testing --prerelease

The meta package (Tracon) does not reference this package. Tracon.Testing is referenced only from your test project, not from your production application.

Quick start

[Fact]
public async Task Tool_is_called_when_order_status_is_asked()
{
    var provider = new FakeModelProvider()
        .CallsTool("get_order_status", new { orderId = "ORD-7" })
        .EchoesUserMessage();

    await using var host = await TraconTestHost.StartAsync(options =>
    {
        options.ModelProvider = provider;
        options.ConfigureTracon = builder => builder
            .AddToolsFrom(typeof(OrderTools))
            .AddAgent(new AgentDefinition
            {
                Name = "support",
                Instructions = "Give a short answer.",
                Model = new ModelBinding { Provider = provider.Name, Model = "fake-model" },
                ToolNames = ["get_order_status"],
                Origin = AgentDefinitionOrigin.Code,
            });
    });

    var run = await host.RunAsync("support", "Where is ORD-7?");

    run.ShouldHaveCompleted()
       .ShouldHaveCalledTool("get_order_status", times: 1)
       .ShouldHaveOutputContaining("Echo:");
}

FakeModelProvider

Each model has its own ordered response queue. A call pops the next step in the queue; once the queue is drained, every subsequent call returns the default behavior (a fixed text, or the echo of the last user message via EchoesUserMessage()). This is lasting for the provider's lifetime: it does not infer "which tool was already called" by scanning message history.

// Default queue: simple scenarios using a single model.
var provider = new FakeModelProvider()
    .RespondsWith("first response", "second response")
    .EchoesUserMessage();          // once the queue is drained

// Per-model queue: different models of the same provider (e.g. a router
// and the sub-agent it hands off to) must behave INDEPENDENTLY.
var routing = new FakeModelProvider()
    .ForModel("router-model", cfg => cfg
        .CallsTool("background_agents_start_task", new { agentName = "researcher" })
        .CallsTool("background_agents_wait_for_first_completion", new { taskIds = new[] { 1 } })
        .EchoesUserMessage())
    .ForModel("researcher-model", cfg => cfg
        .RespondsWith("research complete"));

TraconTestHost

Built on WebApplication.CreateSlimBuilder() + UseTestServer() — Microsoft.AspNetCore.Mvc.Testing's WebApplicationFactory<T> is not used, because it requires an entry-point assembly and locks the consumer into a hosting model. In-memory stores are a first-class implementation , so the host needs no database.

await using var host = await TraconTestHost.StartAsync(options =>
{
    options.ModelProvider = new FakeModelProvider().EchoesUserMessage();
    options.ConfigureTracon = builder => builder.AddAgent(...);
});

// Raw HTTP access is also possible:
using var response = await host.Client.GetAsync("/tracon/api/agents");

Your tool must not expect a dependency from DI

AIFunctionArguments.Services is empty in MAF's run pipeline (Microsoft.Extensions.AI.EmptyServiceProvider). If a tool needs a dependency, that dependency is taken at setup time:

// WRONG: resolving from arguments.Services inside the tool body returns
// null at run time.
public static class OrderTools
{
    [TraconTool]
    public static string GetOrderStatus(string orderId, AIFunctionArguments arguments)
    {
        var repo = arguments.Services!.GetRequiredService<IOrderRepository>(); // null
        ...
    }
}

// CORRECT: the dependency is taken in the constructor, the tool is
// registered through a factory.
public sealed class OrderTools(IOrderRepository repository)
{
    public string GetOrderStatus(string orderId) => repository.Find(orderId);
}

services.AddSingleton(provider =>
{
    var tools = new OrderTools(provider.GetRequiredService<IOrderRepository>());
    return new TraconToolRegistration(
        AIFunctionFactory.Create(tools.GetOrderStatus, "get_order_status"));
});

This trap was measured: an isolated probe program did not prove the real pipeline. TraconTestHost builds the real pipeline, not a separate probe — so this failure shows up in tests the exact same way.

RunAssertions

run.ShouldHaveCompleted();
run.ShouldHaveCalledTool("refund_order");
run.ShouldHaveCalledTool("refund_order", times: 1);
run.ShouldNotHaveCalledTool("delete_account");
run.ShouldHaveFailedWith("content_filtered");
run.ShouldHaveOutputContaining("refunded");

Streaming runs are covered too: run_events fills in on the streaming path as well, no separate type is needed.

Dependencies

The package depends on Tracon.Core and Tracon.AspNetCore; the in-memory host fixture needs the package that builds the endpoints. Tracon.AspNetCore is the only package that carries prerelease MAF packages; since Tracon.Testing depends on it, it inherits those prerelease dependencies transitively. This is acceptable — the test package is not in the production dependency graph.

The package takes no test framework dependency.

Licence: PolyForm Small Business 1.0.0 - free below 100 people and 1,000,000 USD (2019, inflation adjusted) revenue; a commercial licence applies above that. Terms ship in the package as LICENSE.md. Details: https://tracon.dev/reference/licensing/

Product 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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-preview.3 42 9/24/2026
1.0.0-preview.2 75 9/20/2026
1.0.0-preview.1 60 9/20/2026