TestHost.Abstracts
2.0.0
dotnet add package TestHost.Abstracts --version 2.0.0
NuGet\Install-Package TestHost.Abstracts -Version 2.0.0
<PackageReference Include="TestHost.Abstracts" Version="2.0.0" />
<PackageVersion Include="TestHost.Abstracts" Version="2.0.0" />
<PackageReference Include="TestHost.Abstracts" />
paket add TestHost.Abstracts --version 2.0.0
#r "nuget: TestHost.Abstracts, 2.0.0"
#:package TestHost.Abstracts@2.0.0
#addin nuget:?package=TestHost.Abstracts&version=2.0.0
#tool nuget:?package=TestHost.Abstracts&version=2.0.0
TestHost.Abstracts
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:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using TestHost.Abstracts;
using TestHost.Abstracts.Logging;
public class TestApplication : TestHostApplication
{
protected override void ConfigureApplication(HostApplicationBuilder builder)
{
base.ConfigureApplication(builder);
// Add configuration sources
builder.Configuration.AddUserSecrets<TestApplication>();
// Configure logging with memory logger
builder.Logging.AddMemoryLogger();
// Register your services
builder.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.
using Microsoft.Extensions.DependencyInjection;
using TestHost.Abstracts.Logging;
public class MyServiceTests
{
[ClassDataSource<TestApplication>(Shared = SharedType.PerAssembly)]
public required TestApplication Application { get; init; }
[Test]
public async Task TestServiceBehavior()
{
// Arrange - Get service from DI container
var service = Application.Services.GetRequiredService<IMyService>();
// Act
service.DoSomething();
// Assert - Verify behavior
Assert.That(service.SomeProperty).IsTrue();
// Assert on log messages
var memoryLogger = Application.Services.GetService<MemoryLoggerProvider>();
var logs = memoryLogger?.Logs();
Assert.That(logs).Contains(log => log.Message.Contains("Expected log message"));
}
}
Core Components
ITestHostApplication
The primary interface for test host applications:
public interface ITestHostApplication : IAsyncDisposable
{
/// <summary>
/// Gets the host for the tests.
/// </summary>
IHost Host { get; }
/// <summary>
/// Gets the services configured for this test host.
/// </summary>
IServiceProvider Services { 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
public class TestApplication : TestHostApplication
{
// Customize builder settings
protected override HostApplicationBuilderSettings? CreateBuilderSettings()
{
return new HostApplicationBuilderSettings
{
EnvironmentName = "Testing"
};
}
// Configure the application
protected override void ConfigureApplication(HostApplicationBuilder builder)
{
base.ConfigureApplication(builder);
// Your configuration here
}
// Custom host creation (advanced)
protected override IHost CreateHost()
{
// Custom host creation logic
return base.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
protected override void ConfigureApplication(HostApplicationBuilder builder)
{
base.ConfigureApplication(builder);
// Add memory logger with default settings
builder.Logging.AddMemoryLogger();
// Or with custom settings
builder.Logging.AddMemoryLogger(options =>
{
options.MinimumLevel = LogLevel.Debug;
options.Capacity = 2048;
options.Filter = (category, level) => category.StartsWith("MyApp");
});
}
Querying Logs
// Get the memory logger provider
var memoryLogger = Application.Services.GetService<MemoryLoggerProvider>();
// Get all logs
var allLogs = memoryLogger?.Logs();
// Get logs by category
var categoryLogs = memoryLogger?.Logs("MyApp.Services.MyService");
// Get logs by level (warning and above)
var warningLogs = memoryLogger?.Logs(LogLevel.Warning);
// Clear logs between tests
memoryLogger?.Clear();
Asserting on Logs
[Test]
public async Task VerifyLogging()
{
// Arrange
var service = Application.Services.GetRequiredService<IMyService>();
var logger = Application.Services.GetService<MemoryLoggerProvider>();
// Act
service.PerformAction();
// Assert
var logs = logger?.Logs();
await Assert.That(logs).IsNotEmpty();
await Assert.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 createdLogLevel- The log level of the entry (Trace, Debug, Information, Warning, Error, Critical)EventId- Event identifier associated with the log entryCategory- Category name of the logger that created this entryMessage- Formatted log messageException- 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
using Testcontainers.MsSql;
public class TestApplication : TestHostApplication, IAsyncInitializer
{
private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder()
.WithImage("mcr.microsoft.com/mssql/server:2022-latest")
.WithPassword("P@ssw0rd123!")
.Build();
public async Task InitializeAsync()
{
await _msSqlContainer.StartAsync();
}
protected override void ConfigureApplication(HostApplicationBuilder builder)
{
base.ConfigureApplication(builder);
var connectionString = _msSqlContainer.GetConnectionString();
builder.Services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(connectionString));
}
public override async ValueTask DisposeAsync()
{
await _msSqlContainer.DisposeAsync();
await base.DisposeAsync();
}
}
Database Initialization with Hosted Services
public class DatabaseInitialize : IHostedService
{
private readonly IServiceProvider _serviceProvider;
public DatabaseInitialize(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
using var scope = _serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<MyDbContext>();
await context.Database.EnsureCreatedAsync(cancellationToken);
// Seed test data
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
// Register in ConfigureApplication
builder.Services.AddHostedService<DatabaseInitialize>();
Write Database Tests
public class DatabaseTests
{
[ClassDataSource<TestApplication>(Shared = SharedType.PerAssembly)]
public required TestApplication Application { get; init; }
[Test]
public async Task GetUser_WithValidId_ReturnsUser()
{
// Arrange
var dbContext = Application.Services.GetRequiredService<SampleDataContext>();
// Act
var user = await dbContext.Users.FindAsync([1]);
// Assert
await Assert.That(user).IsNotNull();
await Assert.That(user.Name).IsEqualTo("Test User 1");
await Assert.That(user.Email).IsEqualTo("user1@test.com");
}
[Test]
public async Task GetAllUsers_ReturnsSeededUsers()
{
// Arrange
var dbContext = Application.Services.GetRequiredService<SampleDataContext>();
// Act
var users = await dbContext.Users.ToListAsync();
// Assert
await Assert.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.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Extensions.Hosting (>= 10.0.0)
-
net10.0
- Microsoft.Extensions.Hosting (>= 10.0.0)
-
net8.0
- Microsoft.Extensions.Hosting (>= 10.0.0)
-
net9.0
- Microsoft.Extensions.Hosting (>= 10.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.