Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .docfx/Dockerfile.docfx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG NGINX_VERSION=1.31.0-alpine
ARG NGINX_VERSION=1.31.2-alpine

FROM --platform=$BUILDPLATFORM nginx:${NGINX_VERSION} AS base
RUN rm -rf /usr/share/nginx/html/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
uid: Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features
summary: *content
---
The `Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features` namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the `Microsoft.AspNetCore.Http.Features` namespace.

Simulate the ASP.NET Core HTTP request and response feature surfaces in unit tests without running a real server. The `Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features` namespace provides `FakeHttpRequestFeature` and `FakeHttpResponseFeature`, which implement `IHttpRequestFeature` and `IHttpResponseFeature` with settable properties backed by default values. Use these fakes when your test code reads request headers, the request body, the response status code, or response headers from the `IFeatureCollection` on `HttpContext.Features`.

Start with `FakeHttpRequestFeature` when you need to control the HTTP method, path, query string, or request body in isolation. Use `FakeHttpResponseFeature` when the code under test writes status codes, response headers, or the response body.

[!INCLUDE [availability-modern](../../includes/availability-modern.md)]

Complements: [Microsoft.AspNetCore.Http.Features namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.features) 🔗
Complements: [Microsoft.AspNetCore.Http.Features namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.features) 🔗
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
uid: Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http
summary: *content
---
The `Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http` namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the `Microsoft.AspNetCore.Http` namespace.

Isolate unit tests from the real `IHttpContextAccessor` and `HttpContext` pipeline. The `Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http` namespace provides `FakeHttpContextAccessor`, a test double that implements `IHttpContextAccessor` with settable `HttpContext` and `HttpContextFactory` properties, letting you simulate request context without hosting a full server. Use this type when your tested code depends on `IHttpContextAccessor` and you need deterministic, fast test setup.

[!INCLUDE [availability-modern](../../includes/availability-modern.md)]

Complements: [Microsoft.AspNetCore.Http namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http) 🔗
Complements: [Microsoft.AspNetCore.Http namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http) 🔗
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,58 @@
uid: Codebelt.Extensions.Xunit.Hosting.AspNetCore
summary: *content
---
The `Codebelt.Extensions.Xunit.Hosting.AspNetCore` namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the `Microsoft.AspNetCore.TestHost` namespace.

Exercise an ASP.NET Core application's real entry point, dependency-injection graph, middleware pipeline, and endpoints through an in-memory `TestServer`. The `Codebelt.Extensions.Xunit.Hosting.AspNetCore` namespace can bootstrap modern minimal hosting and conventional `Startup` applications, apply test-only web-host configuration, and return either an owned test context or a reusable xUnit fixture.

For a focused endpoint or service-override test, start with `WebApplicationTestFactory.Create<TEntryPoint>` or its one-request `RunAsync<TEntryPoint>` convenience. Use `WebApplicationTest<TEntryPoint, TFixture>` with `BlockingManagedWebApplicationFixture<TEntryPoint>` when several tests should share the bootstrapped application. Reach for `WebHostTestFactory` or `MinimalWebHostTestFactory` when the test defines its own pipeline instead of loading an existing application.

[!INCLUDE [availability-modern](../../includes/availability-modern.md)]

Complements: [Microsoft.AspNetCore.TestHost namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.testhost) 🔗

### Fixture Naming Convention

ASP.NET Core host fixtures follow the same lifecycle naming convention as the hosting package:

|Prefix|Convention|
|---|---|
|`Managed`|The fixture owns host creation, configuration, startup and disposal using the default host runner.|
|`SelfManaged`|The fixture owns host creation and configuration, but leaves host startup to the test.|
|`BlockingManaged`|The fixture owns the host lifecycle and starts the host synchronously before returning control to the test.|

Application-entry-point fixtures use the `BlockingManaged` prefix by default. ASP.NET Core application tests expose a `TestServer`, and callers should receive a started server after fixture initialization. Use `BlockingManagedWebApplicationFixture<TEntryPoint>` when testing an existing ASP.NET Core application entry point with `TestServer`.

`BlockingManagedWebHostFixture` remains the opt-in blocking variant for the lower-level web host fixture family. The application-entry-point fixture is named `BlockingManagedWebApplicationFixture<TEntryPoint>` directly because this API is blocking by convention from its first release.

### Extension Methods

|Type|Ext|Methods|
|--:|:-:|---|
Complements: [ASP.NET Core integration tests](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-10.0) · [WebApplicationFactory<TEntryPoint>](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.testing.webapplicationfactory-1?view=aspnetcore-10.0) · [Microsoft.AspNetCore.TestHost namespace](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.testhost) 🔗

### Choose an ASP.NET Core Testing Path

|When you need to|Start with|Why|
|---|---|---|
|Bootstrap an existing ASP.NET Core application for one focused test|`WebApplicationTestFactory.Create<TEntryPoint>`|Returns an owned `IHostTest` whose host exposes the application's `TestServer`, services, configuration, and environment.|
|Send one request to an existing application|`WebApplicationTestFactory.RunAsync<TEntryPoint>`|Combines application startup, `HttpClient` creation, request execution, and cleanup in one call.|
|Share an existing application across an xUnit test class|`WebApplicationTest<TEntryPoint, TFixture>` with `BlockingManagedWebApplicationFixture<TEntryPoint>`|Uses xUnit fixture lifetime while keeping the real application entry point and `TestServer`.|
|Define services and middleware entirely inside the test|`WebHostTestFactory` or `MinimalWebHostTestFactory`|Builds a purpose-specific in-memory pipeline without loading an application project.|
|Attach observers or change state before startup|A `SelfManaged` web fixture|Builds the host and pipeline but leaves startup to the test.|

### Compared with WebApplicationFactory

`WebApplicationTestFactory` is an alternative integration-test entry point, not a drop-in replacement for `Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>`.

|Concern|`WebApplicationTestFactory`|Microsoft `WebApplicationFactory<TEntryPoint>`|
|---|---|---|
|Acquisition|Static `Create<TEntryPoint>` or `RunAsync<TEntryPoint>` returns a Codebelt test context.|Instantiate or inject a factory, then call `CreateClient`.|
|Customization|Pass an `IWebHostBuilder` callback at the call site.|Subclass and override `ConfigureWebHost`, or compose with `WithWebHostBuilder`.|
|Sharing|Use Codebelt's `WebApplicationTest` and fixture types when tests should share a context.|Commonly shared directly through xUnit `IClassFixture<WebApplicationFactory<TEntryPoint>>`.|
|Lifecycle|The caller disposes the returned context, or delegates ownership to a Codebelt fixture.|The factory owns its `TestServer` and clients and is disposed by the caller or xUnit fixture lifecycle.|
|Scope|Matches Codebelt's equivalent entry-point pattern for console, worker, and Generic Host applications.|Purpose-built for ASP.NET Core applications and includes MVC-testing conventions such as client options and content-root discovery.|

### Fixture Naming Convention

ASP.NET Core host fixtures follow the same lifecycle naming convention as the hosting package:

|Prefix|Convention|
|---|---|
|`Managed`|The fixture owns host creation, configuration, startup and disposal using the default host runner.|
|`SelfManaged`|The fixture owns host creation and configuration, but leaves host startup to the test.|
|`BlockingManaged`|The fixture owns the host lifecycle and starts the host synchronously before returning control to the test.|

Application-entry-point fixtures use the `BlockingManaged` prefix by default. ASP.NET Core application tests expose a `TestServer`, and callers receive a started server after fixture initialization. Use `BlockingManagedWebApplicationFixture<TEntryPoint>` when testing an existing ASP.NET Core application entry point with `TestServer`.

`BlockingManagedWebHostFixture` remains the opt-in blocking variant for the lower-level web host fixture family. The application-entry-point fixture is named `BlockingManagedWebApplicationFixture<TEntryPoint>` directly because this API is blocking by convention from its first release.

### Extension Members

|Type|Ext|Methods|
|--:|:-:|---|
|HttpClient|⬇️|`ToHttpResponseMessageAsync`|
|IHostApplicationBuilder|⬇️|`ToHostBuilder`|
|IServiceCollection|⬇️|`AddFakeHttpContextAccessor`|
|IWebApplicationFixture&lt;TEntryPoint&gt;|⬇️|`HasValidState<TEntryPoint>`|
|IWebHostFixture|⬇️|`HasValidState`|
|IWebMinimalHostFixture|⬇️|`HasValidState`|
61 changes: 39 additions & 22 deletions .docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,45 @@
uid: Codebelt.Extensions.Xunit.Hosting
summary: *content
---
The `Codebelt.Extensions.Xunit.Hosting` namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the `Xunit.Abstractions` namespace.

Exercise a real .NET application entry point, hosted service, or dependency-injection graph without rebuilding its host setup inside the test project. The `Codebelt.Extensions.Xunit.Hosting` namespace applies the same test model to console apps, workers, and Generic Host applications: bootstrap the application's `Program` assembly, customize the host for the scenario, inspect configuration and services, and dispose the test host through one `IHostTest` abstraction.

For a focused test, start with `ApplicationTestFactory.Create<TEntryPoint>`. It is the non-web counterpart to the entry-point pattern commonly associated with ASP.NET Core's `WebApplicationFactory<TEntryPoint>`: the generic argument identifies the application assembly, while the returned test context exposes the resulting host, configuration, and environment. Use the fixture/base-class form when several xUnit tests should share that application context.

[!INCLUDE [availability-default](../../includes/availability-default.md)]

Complements: [xUnit: Shared Context between Tests](https://xunit.net/docs/shared-context) 🔗

### Fixture Naming Convention

Host fixtures follow a lifecycle naming convention:

|Prefix|Convention|
|---|---|
|`Managed`|The fixture owns host creation, configuration, startup and disposal using the default host runner.|
|`SelfManaged`|The fixture owns host creation and configuration, but leaves host startup to the test.|
|`BlockingManaged`|The fixture owns the host lifecycle and starts the host synchronously before returning control to the test.|

Application-entry-point fixtures use the `BlockingManaged` prefix by default. Existing application entry points are discovered and built from their `Program` assembly, so tests should receive a ready host after fixture initialization. Use `BlockingManagedApplicationFixture<TEntryPoint>` when testing console, worker or generic host applications from an existing entry point.

### Extension Methods

|Type|Ext|Methods|
|--:|:-:|---|
|ILogger{T}|⬇️|`GetTestStore`|
|IServiceCollection|⬇️|`AddXunitTestOutputHelperAccessor`|
|IServiceProvider|⬇️|`GetRequiredScopedService`|
Complements: [xUnit: Shared Context between Tests](https://xunit.net/docs/shared-context) 🔗

### Choose a Hosting Path

|When you need to|Start with|Why|
|---|---|---|
|Bootstrap an existing console, worker, or Generic Host application for one test|`ApplicationTestFactory.Create<TEntryPoint>`|Runs the application's entry-point setup and returns an owned `IHostTest` context that the caller disposes.|
|Share an existing application across an xUnit test class|`ApplicationTest<TEntryPoint, TFixture>` with `BlockingManagedApplicationFixture<TEntryPoint>`|Moves application startup and disposal into xUnit's fixture lifecycle while retaining configuration and service access.|
|Build a conventional Generic Host entirely inside the test|`HostTestFactory`|Configures `IServiceCollection` and `IHostBuilder` directly without requiring an application entry point.|
|Build with the modern `IHostApplicationBuilder` model|`MinimalHostTestFactory`|Keeps minimal-host tests focused on services and application-builder configuration.|
|Configure the host now but decide when it starts|A `SelfManaged` fixture|Leaves startup under test control so observers and pre-start assertions can be attached first.|

### Fixture Naming Convention

Host fixtures follow a lifecycle naming convention:

|Prefix|Convention|
|---|---|
|`Managed`|The fixture owns host creation, configuration, startup and disposal using the default host runner.|
|`SelfManaged`|The fixture owns host creation and configuration, but leaves host startup to the test.|
|`BlockingManaged`|The fixture owns the host lifecycle and starts the host synchronously before returning control to the test.|

Application-entry-point fixtures use the `BlockingManaged` prefix by default. Existing application entry points are discovered and built from their `Program` assembly, so tests receive a ready host after fixture initialization. Use `BlockingManagedApplicationFixture<TEntryPoint>` when testing a console, worker, or Generic Host application from an existing entry point.

### Extension Members

|Type|Ext|Methods|
|--:|:-:|---|
|IApplicationFixture&lt;TEntryPoint&gt;|⬇️|`HasValidState<TEntryPoint>`|
|IGenericHostFixture|⬇️|`HasValidState`|
|ILogger|⬇️|`GetTestStore`|
|ILogger&lt;T&gt;|⬇️|`GetTestStore<T>`|
|IMinimalHostFixture|⬇️|`HasValidState`|
|IServiceCollection|⬇️|`AddXunitTestLogging` · `AddXunitTestLoggingOutputHelperAccessor` · `AddXunitTestLoggingOutputHelperAccessor<T>`|
|IServiceProvider|⬇️|`GetRequiredScopedService<T>`|
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
uid: Codebelt.Extensions.Xunit.Hosting.ApplicationFixtureExtensions
example:
- *content
---

The following example validates that an `IApplicationFixture<TEntryPoint>` has been fully initialized with host, configure callback, and configure-host callback by calling `HasValidState<TEntryPoint>` on the fixture. This is useful in assertion helpers to confirm the fixture was set up correctly before running the test scenario.

```csharp
using Codebelt.Extensions.Xunit.Hosting;

namespace HostFixtureTests;

public class HostStateGuard
{
public bool EnsureFixtureIsReady<TEntryPoint>(IApplicationFixture<TEntryPoint> fixture) where TEntryPoint : class
{
return fixture.HasValidState<TEntryPoint>();
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
uid: Codebelt.Extensions.Xunit.Hosting.ApplicationHostFactory
example:
- *content
---

The test project references a worker application's entry-point assembly. `ApplicationHostFactory` captures the host built by that entry point and applies a test-only service override; because this lower-level factory returns the host directly, the caller starts, stops, and disposes it explicitly.

```csharp
using System.Threading.Tasks;
using Codebelt.Extensions.Xunit.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace WorkerApp.Tests;

public sealed class ApplicationHostFactoryExample
{
public async Task<string> StartWithTestIdentityAsync()
{
using IHost host = ApplicationHostFactory.Create<WorkerProgram>(builder =>
{
builder.ConfigureServices(services =>
services.AddSingleton(new WorkerIdentity("Test inventory worker")));
});

await host.StartAsync().ConfigureAwait(false);
var identity = host.Services.GetRequiredService<WorkerIdentity>();
await host.StopAsync().ConfigureAwait(false);

return identity.Name;
}
}

public sealed record WorkerIdentity(string Name);

public sealed class WorkerProgram
{
public static void Main(string[] args)
{
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton(new WorkerIdentity("Inventory worker"));

using var host = builder.Build();
host.Run();
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
uid: Codebelt.Extensions.Xunit.Hosting.ApplicationTestFactory
example:
- *content
---

The test project references a worker application whose entry point registers `WorkerIdentity`. `ApplicationTestFactory` runs that application's real host setup, then exposes its services and environment through an owned test context so the test can verify application behavior without recreating `Program` configuration.

```csharp
using Codebelt.Extensions.Xunit.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xunit;

namespace WorkerApp.Tests;

public sealed class ApplicationTestFactoryExample
{
[Fact]
public void Create_BootstrapsWorkerApplicationServices()
{
using var application = ApplicationTestFactory.Create<WorkerProgram>();

var identity = application.Host.Services.GetRequiredService<WorkerIdentity>();

Assert.Equal("Inventory worker", identity.Name);
Assert.Equal(Environments.Development, application.Environment.EnvironmentName);
}
}

public sealed record WorkerIdentity(string Name);

public sealed class WorkerProgram
{
public static void Main(string[] args)
{
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton(new WorkerIdentity("Inventory worker"));

using var host = builder.Build();
host.Run();
}
}
```
Loading
Loading