Overview
Codebelt.Extensions.Xunit.Hosting extends Codebelt.Extensions.Xunit with a real IHost and Microsoft Dependency Injection inside xUnit tests. It builds and starts a generic host configured the way a production application would be, so a test can resolve services, exercise configuration, and inspect the hosting environment instead of hand-wiring a service provider.
The package offers two entry styles. A base-class style (HostTest<T> and MinimalHostTest<T>) follows the Startup convention and the xUnit class-fixture model, while a factory style (HostTestFactory and MinimalHostTestFactory) creates a configured host inline for shorter tests. It also adds a logging provider that captures log entries in memory for assertions.
Key APIs
HostTest<T> is the abstract base class for dependency-injection tests, where T is an IGenericHostFixture supplied through xUnit's IClassFixture<T>. You override the abstract ConfigureServices(IServiceCollection services) to register services and may override ConfigureHost(IHostBuilder hb) to adjust host defaults; the resolved Host, Configuration, and Environment are then exposed as properties. MinimalHostTest<T> is the equivalent for the minimal IHostApplicationBuilder style and overrides ConfigureHost(IHostApplicationBuilder hb).
HostTestFactory.Create builds an IHostTest inline from optional Action<IServiceCollection> and Action<IHostBuilder> delegates, returning a disposable host you configure without declaring a fixture class. HostTestFactory.CreateWithHostBuilderContext is the overload whose service callback receives a HostBuilderContext, and MinimalHostTestFactory mirrors both methods for the minimal hosting model.
ManagedHostFixture is the default IGenericHostFixture implementation. It configures content root, the Development environment, JSON and environment-variable configuration sources, and (on .NET 9 and later) scope validation, then starts the host through a runner callback. ManagedMinimalHostFixture provides the same behavior over Host.CreateApplicationBuilder, and the SelfManagedHostFixture and SelfManagedMinimalHostFixture variants replace the runner with a no-op so the caller controls startup.
ServiceCollectionExtensions.AddXunitTestLogging registers a unit-test-optimized logging provider, optionally bound to an ITestOutputHelper so log output also reaches the test runner. AddXunitTestLoggingOutputHelperAccessor registers an ITestOutputHelperAccessor so the active output helper can be assigned per test, which is the recommended approach when the host outlives a single test method.
LoggerExtensions.GetTestStore returns the ITestStore<XunitTestLoggerEntry> associated with a logger that was configured through AddXunitTestLogging, with a generic GetTestStore<T>(ILogger<T>) overload scoped to that logger's category. XunitTestLoggerEntry is the captured record exposing Severity, Id, and Message, where ToString returns the message.
Basic usage
using System.Linq;
using Codebelt.Extensions.Xunit;
using Codebelt.Extensions.Xunit.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Xunit;
namespace InventoryTests;
public class InventoryServiceTest : Test
{
public InventoryServiceTest(ITestOutputHelper output) : base(output)
{
}
[Fact]
public void Reserve_ShouldLogReservation()
{
using var test = HostTestFactory.Create(services =>
{
services.AddXunitTestLogging(TestOutput);
services.AddSingleton<InventoryService>();
});
var service = test.Host.Services.GetRequiredService<InventoryService>();
service.Reserve("SKU-100", 3);
var entries = test.Host.Services.GetRequiredService<ILogger<InventoryService>>().GetTestStore();
TestOutput.WriteLines(entries.Query().Select(entry => entry.Message));
Assert.Equal("Information: Reserved 3 unit(s) of SKU-100", entries.Query().Single().Message);
}
}
public sealed class InventoryService
{
private readonly ILogger<InventoryService> _logger;
public InventoryService(ILogger<InventoryService> logger) => _logger = logger;
public void Reserve(string sku, int quantity) => _logger.LogInformation("Reserved {Quantity} unit(s) of {Sku}", quantity, sku);
}
Use this pattern when a service under test depends on the dependency-injection container and you want to assert on what it logged rather than on a mock. It matters because the host wires up real logging, and GetTestStore lets the test query the captured XunitTestLoggerEntry items produced by the service through the standard ILogger<T> API.
Installation
dotnet add package Codebelt.Extensions.Xunit.Hosting
Usage guidance
Choose this package when a test needs Microsoft Dependency Injection, configuration, or a started IHost and the system under test is not an ASP.NET Core request pipeline. Prefer HostTestFactory for short, self-contained tests and the HostTest<T> base class when you need a shared fixture or scoped lifetimes across several tests; if you are testing middleware, controllers, or anything that needs a TestServer and HttpClient, use Codebelt.Extensions.Xunit.Hosting.AspNetCore instead.