# ASP.NET Core Integration

Use these patterns for ASP.NET Core integration tests built on `TUnit.AspNetCore`.

## Prefer TUnit's ASP.NET Base Types

- Derive factories from `TestWebApplicationFactory<TEntryPoint>`, not directly from `WebApplicationFactory<TEntryPoint>`.
- Derive test bases from `WebApplicationTest<TFactory, TEntryPoint>`.
- This preserves TUnit tracing, per-test logging correlation, and test context propagation.

```csharp
public sealed class AppFactory : TestWebApplicationFactory<Program>
{
}

public abstract class TestsBase : WebApplicationTest<AppFactory, Program>
{
}
```

## Factory vs Test Responsibilities

Use the factory for shared defaults that apply to every test in the class/session:

- shared environment selection
- shared service replacement defaults
- shared startup-time configuration defaults

Use the `WebApplicationTest` overrides for per-test behavior:

- `SetupAsync()` for async preparation before factory creation
- `ConfigureTestConfiguration(...)` for per-test configuration overrides
- `ConfigureTestServices(...)` for per-test DI overrides
- `ConfigureWebHostBuilder(...)` only as an escape hatch

## Configuration Timing Matters

If configuration must be visible while `Program.cs` is building the app, put it in:

- `TestWebApplicationFactory.ConfigureStartupConfiguration(...)`

Do not rely on `ConfigureWebHost(...).ConfigureAppConfiguration(...)` for settings that must exist during application startup. That path is too late for some startup reads.

```csharp
public sealed class AppFactory : TestWebApplicationFactory<Program>
{
    protected override void ConfigureStartupConfiguration(IConfigurationBuilder configurationBuilder)
    {
        configurationBuilder.AddInMemoryCollection(new Dictionary<string, string?>
        {
            ["Feature:Enabled"] = bool.TrueString,
        });
    }
}
```

For per-test values discovered in `SetupAsync()`, use `ConfigureTestConfiguration(...)`:

```csharp
public abstract class TestsBase : WebApplicationTest<AppFactory, Program>
{
    protected string SchemaName { get; private set; } = null!;

    protected override async Task SetupAsync()
    {
        SchemaName = GetIsolatedName("schema");
        await CreateSchemaAsync(SchemaName);
    }

    protected override void ConfigureTestConfiguration(IConfigurationBuilder config)
    {
        config.AddInMemoryCollection(new Dictionary<string, string?>
        {
            ["Database:Schema"] = SchemaName,
        });
    }
}
```

## Per-Test Isolation

For shared mutable resources, isolate by test.

- use `GetIsolatedName("...")` for tables, schemas, queues, topics
- use `GetIsolatedPrefix()` for cache keys, blob prefixes, Redis keys

For EF Core schema isolation, prefer:

- `SchemaName = GetIsolatedName("schema")`
- creating the schema in `SetupAsync()`
- `IModelCacheKeyFactory` keyed by schema
- dropping the schema in `[After(Test)]`

## Using the Factory in Tests

`Factory` is a per-test `TracedWebApplicationFactory<TEntryPoint>`.

Use:

- `Factory.CreateClient()` for normal requests
- `Factory.Services` for resolving services scoped to the isolated test factory

If you need advanced `WebApplicationFactory` APIs such as `WithWebHostBuilder(...)`, go through `Factory.Inner`:

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

var client = customFactory.CreateClient();
```

## Service Replacement

When overriding app services in tests, remove or replace existing registrations instead of stacking duplicates.

```csharp
protected override void ConfigureTestServices(IServiceCollection services)
{
    services.ReplaceService<IEmailSender>(new FakeEmailSender());
}
```

## Cancellation

Prefer injecting `CancellationToken` into test methods and hooks.

For code inside `WebApplicationTest`, use the current test execution token if needed:

```csharp
protected CancellationToken CancellationToken => TestContext.Current!.Execution.CancellationToken;
```
