Skip to content

Integration TUnit

Aryeh Citron edited this page Apr 13, 2026 · 11 revisions

Example project: A complete working example is available at Example.Api/tests/Example.Api.Tests.Component.TUnit/.


Overview

This guide walks you through integrating TestTrackingDiagrams with TUnit. After completing this guide, your TUnit tests will automatically generate:

  • PlantUML sequence diagrams from HTTP traffic between your service and its dependencies
  • HTML reports with embedded diagrams
  • YAML specification files

Prerequisites

  • .NET 10.0 SDK or later
  • An ASP.NET Core API project to test (your "Service Under Test")
  • Basic familiarity with TUnit

Step 1: Create the Test Project

Create a new console project (TUnit requires OutputType=Exe):

dotnet new console -n MyApi.Tests.Component

Step 2: Install NuGet Packages

dotnet add package TestTrackingDiagrams.TUnit
dotnet add package TUnit
dotnet add package Microsoft.AspNetCore.Mvc.Testing

Your <ItemGroup> should look like this:

<PropertyGroup>
    <OutputType>Exe</OutputType>
    <IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
    <PackageReference Include="TestTrackingDiagrams.TUnit" Version="2.0.78-beta" />
    <PackageReference Include="TUnit" Version="1.33.0" />
    <PackageReference Include="coverlet.collector" Version="8.0.1">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
</ItemGroup>

Note: TUnit uses Microsoft.Testing.Platform (not Microsoft.NET.Test.Sdk). The TUnit meta package handles the test runner configuration automatically.


Step 3: Create the Test Run Setup/Teardown

TUnit uses [Before(Assembly)] / [After(Assembly)] hooks for global setup and teardown.

Create Infrastructure/TestRun.cs:

using TestTrackingDiagrams;
using TestTrackingDiagrams.TUnit;
using TUnit.Core;

namespace MyApi.Tests.Component.Infrastructure;

public class TestRun : DiagrammedTestRun
{
    [Before(Assembly)]
    public static void GlobalSetup(AssemblyHookContext context)
    {
        Setup();
        // Optional: start any HTTP fakes here
    }

    [After(Assembly)]
    public static void GlobalTeardown(AssemblyHookContext context)
    {
        EndRunTime = DateTime.UtcNow;

        // Generate reports when the test run ends
        TUnitReportGenerator.CreateStandardReportsWithDiagrams(
            TestContexts,
            StartRunTime,
            EndRunTime,
            new ReportConfigurationOptions
            {
                SpecificationsTitle = "My API Specifications"
            });

        // Optional: dispose HTTP fakes here
    }
}

Key points:

  • [Before(Assembly)] and [After(Assembly)] run once at the start and end of the test assembly.
  • The AssemblyHookContext parameter is required by TUnit for assembly-level hooks.
  • Call Setup() in [Before(Assembly)] — this records the StartRunTime.

Step 4: Create the Base Fixture

Create Infrastructure/BaseFixture.cs:

using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using TestTrackingDiagrams.TUnit;

namespace MyApi.Tests.Component.Infrastructure;

public abstract class BaseFixture : DiagrammedComponentTest, IDisposable
{
    private static readonly WebApplicationFactory<Program>? SFactory;
    protected HttpClient Client { get; }

    private const string ServiceUnderTestName = "My API";

    static BaseFixture()
    {
        SFactory = new WebApplicationFactory<Program>().WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.TrackDependenciesForDiagrams(new TUnitTestTrackingMessageHandlerOptions
                {
                    CallingServiceName = ServiceUnderTestName,
                    PortsToServiceNames =
                    {
                        { 80, ServiceUnderTestName },
                        { 5001, "Downstream Service A" }
                    }
                });
            });
        });
    }

    protected BaseFixture()
    {
        Client = SFactory!.CreateTestTrackingClient(
            new TUnitTestTrackingMessageHandlerOptions
            {
                FixedNameForReceivingService = ServiceUnderTestName
            });
    }

    public void Dispose() => Client.Dispose();
}

Key points:

  • DiagrammedComponentTest provides an [After(Test)] hook that automatically enqueues the TestContext for report collection after each test.
  • TUnitTestTrackingMessageHandlerOptions uses TUnit's TestContext.Current to resolve the current test's identity.
  • TUnit creates a new instance per test by default (no additional configuration needed).

Step 5: Write Test Scenarios

Tests are written as regular TUnit [Test] methods. Use the [Endpoint] and [HappyPath] attributes to add metadata for the report.

Scenarios/Cake_Feature.cs:

using TestTrackingDiagrams.TUnit;

namespace MyApi.Tests.Component.Scenarios;

[Endpoint("/cake")]
public partial class Cake_Feature
{
    [Test]
    [HappyPath]
    public async Task Calling_Create_Cake_Endpoint_Returns_Cake()
    {
        await Given_a_valid_post_request_for_the_Cake_endpoint();
        await When_the_request_is_sent_to_the_cake_post_endpoint();
        await Then_the_response_should_be_successful();
    }

    [Test]
    public async Task Calling_Create_Cake_Endpoint_Without_Eggs_Returns_Bad_Request()
    {
        await Given_a_valid_post_request_for_the_Cake_endpoint();
        await But_the_request_body_is_missing_eggs();
        await When_the_request_is_sent_to_the_cake_post_endpoint();
        await Then_the_response_http_status_should_be_bad_request();
    }
}

Scenarios/Cake_Feature.steps.cs:

using System.Net;
using System.Net.Http.Json;
using MyApi.Tests.Component.Infrastructure;

namespace MyApi.Tests.Component.Scenarios;

public partial class Cake_Feature : BaseFixture
{
    private HttpResponseMessage? _response;

    private async Task Given_a_valid_post_request_for_the_Cake_endpoint()
    {
        // Build your request using Client
    }

    private async Task But_the_request_body_is_missing_eggs()
    {
        // Modify request
    }

    private async Task When_the_request_is_sent_to_the_cake_post_endpoint()
    {
        _response = await Client.PostAsJsonAsync("cake", /* request */);
    }

    private async Task Then_the_response_should_be_successful()
    {
        _response!.StatusCode.Should().Be(HttpStatusCode.OK);
    }

    private async Task Then_the_response_http_status_should_be_bad_request()
    {
        _response!.StatusCode.Should().Be(HttpStatusCode.BadRequest);
    }
}

Key points:

  • [Endpoint("/cake")] — Sets the endpoint label for this feature group in the report. This attribute maps to TUnit's PropertyAttribute.
  • [HappyPath] — Marks a scenario as a happy path (filterable in the HTML report). This maps to TUnit's CategoryAttribute.
  • Class and method names are converted from underscore-separated / PascalCase to human-readable format in reports.

Step 6: Run the Tests

dotnet run

Note: Since TUnit uses Microsoft.Testing.Platform, you run tests with dotnet run instead of dotnet test. However, dotnet test also works if you have the TUnit meta package installed.

After the tests complete, check the bin/Debug/net10.0/Reports/ folder:

File Description
ComponentSpecificationsWithExamples.html HTML specifications with embedded PlantUML sequence diagrams
FeaturesReport.html HTML test run report with diagrams and execution summary
ComponentSpecifications.yml YAML specifications

Architecture Summary

┌─────────────────────────────────┐
│           TestRun               │  ← [Before(Assembly)] / [After(Assembly)]
│     : DiagrammedTestRun         │     Generates reports in [After(Assembly)]
└─────────────────────────────────┘
              │
              ▼
┌─────────────────────────────────┐
│          BaseFixture            │  ← Creates tracked HttpClient
│   : DiagrammedComponentTest     │     Enqueues TestContext via [After(Test)]
│   IDisposable                   │
└─────────────┬───────────────────┘
              │ inherited by
              ▼
┌─────────────────────────────────┐
│  Cake_Feature : BaseFixture     │  ← Your test class with [Test] methods
└─────────────────────────────────┘

Home


Demo


Getting Started

Common Tasks

Integration Guides

Extensions

Configuration

Features

Reference

Clone this wiki locally