Spensara Spensara Knowledge Base & Documentation Repository

Repository Docs & Knowledge Base

Consolidated Spensara documentation repository (722 Markdown & architecture documents in ../repository)
Built Word Documents (.docx): Spensara Overview (.docx) Capability Assessment (.docx) Brink POS Ingestion Analysis (.docx) The Borg Ingestion Architecture (.docx)

Agent Tooling (71)

Automation workflows, orchestration references, and developer tooling guides.
agent-tooling/.agents/skills/tunit-testing/references/infrastructure-and-troubleshooting.md 4,360 bytes · Updated Oct 23, 2026 10:44 AM EDT
Raw File

Infrastructure And Troubleshooting

Patterns and pitfalls for shared infrastructure, live tests, and common TUnit issues.

Shared Infrastructure Orchestration

Prefer TUnit property injection chains over manual dependency wiring.

  • shared expensive resources should usually use [ClassDataSource<T>(Shared = SharedType.PerTestSession)]
  • dependent infrastructure objects should declare their dependencies as injected properties
  • do not manually new shared dependencies inside another shared fixture unless there is a strong reason
  • do not manually dispose dependencies that TUnit injected for you; each fixture should dispose only what it owns

Example:

csharp
public sealed class DockerNetwork : IAsyncInitializer, IAsyncDisposable
{
    public INetwork Instance { get; } = new NetworkBuilder().Build();

    public Task InitializeAsync() => Instance.CreateAsync();
    public ValueTask DisposeAsync() => Instance.DisposeAsync();
}

public sealed class PostgresDatabase : IAsyncInitializer, IAsyncDisposable
{
    [ClassDataSource<DockerNetwork>(Shared = SharedType.PerTestSession)]
    public required DockerNetwork DockerNetwork { get; init; }

    public PostgreSqlContainer Container => field ??= new PostgreSqlBuilder()
        .WithNetwork(DockerNetwork.Instance)
        .Build();

    public async Task InitializeAsync() => await Container.StartAsync();
    public async ValueTask DisposeAsync() => await Container.DisposeAsync();
}

Live Test Opt-In

If your team wants live tests to stay out of the default dotnet test path, prefer an explicit runtime gate.

If the framework version does not provide a built-in explicit/manual runner switch that works cleanly in your CLI and CI flow, use:

  • Skip.Test(...) gated by an environment variable or custom skip attribute

Example:

csharp
private static void SkipIfLiveTestsDisabled()
{
    if (!string.Equals(Environment.GetEnvironmentVariable("RUN_LIVE_TESTS"), "1", StringComparison.Ordinal))
    {
        Skip.Test("Live tests require RUN_LIVE_TESTS=1.");
    }
}

Apply the gate early:

  • at the start of the test method
  • and, if a test uses setup hooks or containers, at the start of the setup hook too

That avoids spinning up containers or external resources for tests that will be skipped anyway.

Assertion Pitfall: Arrays And Buffers

For arrays and buffers, IsEqualTo(...) may enforce exact equality semantics rather than element-wise equivalence.

For byte[] or similar payloads, prefer:

csharp
await Assert.That(actualBytes).IsEquivalentTo(expectedBytes);

Use IsEqualTo(...) when exact scalar equality or ordered collection equality semantics are intended and supported by the assertion type.

Package Version Pitfall: Central Package Management

When using central package management, watch for transitive requirements introduced by newer TUnit packages.

Example failure mode:

  • TUnit depends on Microsoft.Testing.Extensions.TrxReport >= X
  • Directory.Packages.props pins Microsoft.Testing.Extensions.TrxReport to an older version
  • restore fails with a package downgrade error

Guidance:

  • keep centrally pinned Microsoft.Testing.Platform extensions aligned with the minimum version required by the current TUnit version
  • when a downgrade appears, fix the central version instead of adding local project-level overrides unless there is a specific project isolation reason

Build/Test Lock Contention

Do not run overlapping dotnet build and dotnet test commands against the same outputs in parallel.

Typical symptom:

  • .deps.json / output file lock failures

Preferred workflow:

  • run test projects sequentially when they share build outputs
  • run the final solution build only after test commands finish

Advanced Factory Overrides

Inside WebApplicationTest<TFactory, TEntryPoint>, Factory is a traced wrapper.

If you need raw WebApplicationFactory APIs such as WithWebHostBuilder(...), use Factory.Inner and, if needed, wrap the result back into TracedWebApplicationFactory<TEntryPoint>.

csharp
await using var customFactory = new TracedWebApplicationFactory<Program>(
    Factory.Inner.WithWebHostBuilder(builder =>
    {
        builder.UseSetting("Feature:Enabled", bool.FalseString);
    }));

This preserves TUnit request tracing and log correlation for the derived factory.