Skip to content

Repository files navigation

TestHost.Abstracts

CINuGet VersionNuGet Downloads

A flexible and powerful test host builder for .NET applications that provides abstractions for hosting test applications with dependency injection, configuration, and in-memory logging support.

Features

  • Test Host Abstraction - Base classes and interfaces for creating test host applications
  • Dependency Injection - Full integration with Microsoft.Extensions.DependencyInjection
  • Configuration Support - Leverage Microsoft.Extensions.Configuration for test settings
  • In-Memory Logger - Capture and assert on log messages during tests
  • Async Lifecycle - Proper async initialization and disposal patterns

Installation

Install the package from NuGet:

dotnet add package TestHost.Abstracts

Or via Package Manager Console:

Install-Package TestHost.Abstracts

Quick Start

1. Create a Test Application

Inherit from TestHostApplication to create your test host:

usingMicrosoft.Extensions.Configuration;usingMicrosoft.Extensions.DependencyInjection;usingMicrosoft.Extensions.Hosting;usingTestHost.Abstracts;usingTestHost.Abstracts.Logging;publicclassTestApplication:TestHostApplication{protectedoverridevoidConfigureApplication(HostApplicationBuilderbuilder){base.ConfigureApplication(builder);// Add configuration sourcesbuilder.Configuration.AddUserSecrets<TestApplication>();// Configure logging with memory loggerbuilder.Logging.AddMemoryLogger();// Register your servicesbuilder.Services.AddSingleton<IMyService,MyService>();}}

2. Use in Your Tests

Note: The examples below use TUnit, a modern .NET testing framework. You can also use this library with xUnit, NUnit, or MSTest.

usingMicrosoft.Extensions.DependencyInjection;usingTestHost.Abstracts.Logging;publicclassMyServiceTests{[ClassDataSource<TestApplication>(Shared=SharedType.PerAssembly)]publicrequiredTestApplicationApplication{get;init;}[Test]publicasyncTaskTestServiceBehavior(){// Arrange - Get service from DI containervarservice=Application.Services.GetRequiredService<IMyService>();// Actservice.DoSomething();// Assert - Verify behaviorAssert.That(service.SomeProperty).IsTrue();// Assert on log messagesvarmemoryLogger=Application.Services.GetService<MemoryLoggerProvider>();varlogs=memoryLogger?.Logs();Assert.That(logs).Contains(log =>log.Message.Contains("Expected log message"));}}

Core Components

ITestHostApplication

The primary interface for test host applications:

publicinterfaceITestHostApplication:IAsyncDisposable{/// <summary>/// Gets the host for the tests./// </summary>IHostHost{get;}/// <summary>/// Gets the services configured for this test host./// </summary>IServiceProviderServices{get;}}

TestHostApplication

Base class that implements ITestHostApplication with convenient lifecycle management:

  • Thread-safe Host Creation - Lazy initialization with proper locking
  • Configurable Builder - Override CreateBuilderSettings() to customize host builder settings
  • Application Configuration - Override ConfigureApplication() to configure services and logging
  • Async Disposal - Proper cleanup of host resources

Lifecycle Hooks

publicclassTestApplication:TestHostApplication{// Customize builder settingsprotectedoverrideHostApplicationBuilderSettings?CreateBuilderSettings(){returnnewHostApplicationBuilderSettings{EnvironmentName="Testing"};}// Configure the applicationprotectedoverridevoidConfigureApplication(HostApplicationBuilderbuilder){base.ConfigureApplication(builder);// Your configuration here}// Custom host creation (advanced)protectedoverrideIHostCreateHost(){// Custom host creation logicreturnbase.CreateHost();}}

In-Memory Logger

The in-memory logger captures log messages during test execution for verification and debugging.

Features

  • Capture log entries in memory
  • Query logs by category, log level, or custom filters
  • Thread-safe log collection
  • Configurable capacity and filtering
  • Structured logging support with scopes and state

Adding the Memory Logger

protectedoverridevoidConfigureApplication(HostApplicationBuilderbuilder){base.ConfigureApplication(builder);// Add memory logger with default settingsbuilder.Logging.AddMemoryLogger();// Or with custom settingsbuilder.Logging.AddMemoryLogger(options =>{options.MinimumLevel=LogLevel.Debug;options.Capacity=2048;options.Filter=(category,level)=>category.StartsWith("MyApp");});}

Querying Logs

// Get the memory logger providervarmemoryLogger=Application.Services.GetService<MemoryLoggerProvider>();// Get all logsvarallLogs=memoryLogger?.Logs();// Get logs by categoryvarcategoryLogs=memoryLogger?.Logs("MyApp.Services.MyService");// Get logs by level (warning and above)varwarningLogs=memoryLogger?.Logs(LogLevel.Warning);// Clear logs between testsmemoryLogger?.Clear();

Asserting on Logs

[Test]publicasyncTaskVerifyLogging(){// Arrangevarservice=Application.Services.GetRequiredService<IMyService>();varlogger=Application.Services.GetService<MemoryLoggerProvider>();// Actservice.PerformAction();// Assertvarlogs=logger?.Logs();awaitAssert.That(logs).IsNotEmpty();awaitAssert.That(logs).Contains(log =>log.LogLevel==LogLevel.Information&&log.Message.Contains("Action performed"));}

MemoryLoggerSettings

Configure the memory logger with these options:

  • MinimumLevel - Minimum log level to capture (default: LogLevel.Debug)
  • Capacity - Maximum number of log entries to keep (default: 1024)
  • Filter - Custom filter function for fine-grained control

MemoryLogEntry

Log entries captured include:

  • Timestamp - DateTime when the log entry was created
  • LogLevel - The log level of the entry (Trace, Debug, Information, Warning, Error, Critical)
  • EventId - Event identifier associated with the log entry
  • Category - Category name of the logger that created this entry
  • Message - Formatted log message
  • Exception - Exception associated with the log entry, if any (nullable)
  • State - The state object passed to the logger (nullable)
  • Scopes - Read-only collection of scope values that were active when the log entry was created

Integration Testing with Docker Databases

TestHost.Abstracts works seamlessly with Testcontainers to provide isolated database environments for integration tests. This approach uses IAsyncInitializer to manage container lifecycle and IHostedService to seed the database.

Install Testcontainers

dotnet add package Testcontainers.MsSql

Integration with Test Containers

usingTestcontainers.MsSql;publicclassTestApplication:TestHostApplication,IAsyncInitializer{privatereadonlyMsSqlContainer_msSqlContainer=newMsSqlBuilder().WithImage("mcr.microsoft.com/mssql/server:2022-latest").WithPassword("P@ssw0rd123!").Build();publicasyncTaskInitializeAsync(){await_msSqlContainer.StartAsync();}protectedoverridevoidConfigureApplication(HostApplicationBuilderbuilder){base.ConfigureApplication(builder);varconnectionString=_msSqlContainer.GetConnectionString();builder.Services.AddDbContext<MyDbContext>(options =>options.UseSqlServer(connectionString));}publicoverrideasyncValueTaskDisposeAsync(){await_msSqlContainer.DisposeAsync();awaitbase.DisposeAsync();}}

Database Initialization with Hosted Services

publicclassDatabaseInitialize:IHostedService{privatereadonlyIServiceProvider_serviceProvider;publicDatabaseInitialize(IServiceProviderserviceProvider){_serviceProvider=serviceProvider;}publicasyncTaskStartAsync(CancellationTokencancellationToken){usingvarscope=_serviceProvider.CreateScope();varcontext=scope.ServiceProvider.GetRequiredService<MyDbContext>();awaitcontext.Database.EnsureCreatedAsync(cancellationToken);// Seed test data}publicTaskStopAsync(CancellationTokencancellationToken){returnTask.CompletedTask;}}// Register in ConfigureApplicationbuilder.Services.AddHostedService<DatabaseInitialize>();

Write Database Tests

publicclassDatabaseTests{[ClassDataSource<TestApplication>(Shared=SharedType.PerAssembly)]publicrequiredTestApplicationApplication{get;init;}[Test]publicasyncTaskGetUser_WithValidId_ReturnsUser(){// ArrangevardbContext=Application.Services.GetRequiredService<SampleDataContext>();// Actvaruser=awaitdbContext.Users.FindAsync([1]);// AssertawaitAssert.That(user).IsNotNull();awaitAssert.That(user.Name).IsEqualTo("Test User 1");awaitAssert.That(user.Email).IsEqualTo("user1@test.com");}[Test]publicasyncTaskGetAllUsers_ReturnsSeededUsers(){// ArrangevardbContext=Application.Services.GetRequiredService<SampleDataContext>();// Actvarusers=awaitdbContext.Users.ToListAsync();// AssertawaitAssert.That(users.Count).IsGreaterThanOrEqualTo(2);}}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Unit test host builder

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages