# 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.
