{"id":28619080,"url":"https://github.com/loresoft/testhost.abstracts","last_synced_at":"2026-03-11T04:02:53.688Z","repository":{"id":289196883,"uuid":"970449414","full_name":"loresoft/TestHost.Abstracts","owner":"loresoft","description":"Unit test host builder","archived":false,"fork":false,"pushed_at":"2026-03-04T08:13:42.000Z","size":246,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-04T14:18:14.921Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/loresoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"loresoft"}},"created_at":"2025-04-22T03:20:09.000Z","updated_at":"2026-03-04T08:13:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"83da756e-4999-4ce3-a816-c6d1354474d3","html_url":"https://github.com/loresoft/TestHost.Abstracts","commit_stats":null,"previous_names":["loresoft/test.hosting"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/loresoft/TestHost.Abstracts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FTestHost.Abstracts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FTestHost.Abstracts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FTestHost.Abstracts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FTestHost.Abstracts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/loresoft","download_url":"https://codeload.github.com/loresoft/TestHost.Abstracts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/loresoft%2FTestHost.Abstracts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30370277,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T21:41:54.280Z","status":"online","status_checked_at":"2026-03-11T02:00:07.027Z","response_time":84,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-12T04:20:23.855Z","updated_at":"2026-03-11T04:02:53.678Z","avatar_url":"https://github.com/loresoft.png","language":"C#","readme":"# TestHost.Abstracts\n\n[![CI](https://github.com/loresoft/TestHost.Abstracts/actions/workflows/dotnet.yml/badge.svg)](https://github.com/loresoft/TestHost.Abstracts/actions/workflows/dotnet.yml)\n[![NuGet Version](https://img.shields.io/nuget/v/TestHost.Abstracts.svg?style=flat-square)](https://www.nuget.org/packages/TestHost.Abstracts/)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/TestHost.Abstracts.svg)](https://www.nuget.org/packages/TestHost.Abstracts/)\n\nA 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.\n\n## Features\n\n- **Test Host Abstraction** - Base classes and interfaces for creating test host applications\n- **Dependency Injection** - Full integration with Microsoft.Extensions.DependencyInjection\n- **Configuration Support** - Leverage Microsoft.Extensions.Configuration for test settings\n- **In-Memory Logger** - Capture and assert on log messages during tests\n- **Async Lifecycle** - Proper async initialization and disposal patterns\n\n## Installation\n\nInstall the package from NuGet:\n\n```shell\ndotnet add package TestHost.Abstracts\n```\n\nOr via Package Manager Console:\n\n```powershell\nInstall-Package TestHost.Abstracts\n```\n\n## Quick Start\n\n### 1. Create a Test Application\n\nInherit from `TestHostApplication` to create your test host:\n\n```csharp\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing TestHost.Abstracts;\nusing TestHost.Abstracts.Logging;\n\npublic class TestApplication : TestHostApplication\n{\n    protected override void ConfigureApplication(HostApplicationBuilder builder)\n    {\n        base.ConfigureApplication(builder);\n\n        // Add configuration sources\n        builder.Configuration.AddUserSecrets\u003cTestApplication\u003e();\n        \n        // Configure logging with memory logger\n        builder.Logging.AddMemoryLogger();\n\n        // Register your services\n        builder.Services.AddSingleton\u003cIMyService, MyService\u003e();\n    }\n}\n```\n\n### 2. Use in Your Tests\n\n\u003e **Note:** The examples below use [TUnit](https://github.com/thomhurst/TUnit), a modern .NET testing framework. You can also use this library with xUnit, NUnit, or MSTest.\n\n```csharp\nusing Microsoft.Extensions.DependencyInjection;\nusing TestHost.Abstracts.Logging;\n\npublic class MyServiceTests\n{\n    [ClassDataSource\u003cTestApplication\u003e(Shared = SharedType.PerAssembly)]\n    public required TestApplication Application { get; init; }\n\n    [Test]\n    public async Task TestServiceBehavior()\n    {\n        // Arrange - Get service from DI container\n        var service = Application.Services.GetRequiredService\u003cIMyService\u003e();\n\n        // Act\n        service.DoSomething();\n\n        // Assert - Verify behavior\n        Assert.That(service.SomeProperty).IsTrue();\n\n        // Assert on log messages\n        var memoryLogger = Application.Services.GetService\u003cMemoryLoggerProvider\u003e();\n        var logs = memoryLogger?.Logs();\n        \n        Assert.That(logs).Contains(log =\u003e log.Message.Contains(\"Expected log message\"));\n    }\n}\n```\n\n## Core Components\n\n### ITestHostApplication\n\nThe primary interface for test host applications:\n\n```csharp\npublic interface ITestHostApplication : IAsyncDisposable\n{\n    /// \u003csummary\u003e\n    /// Gets the host for the tests.\n    /// \u003c/summary\u003e\n    IHost Host { get; }\n\n    /// \u003csummary\u003e\n    /// Gets the services configured for this test host.\n    /// \u003c/summary\u003e\n    IServiceProvider Services { get; }\n}\n```\n\n### TestHostApplication\n\nBase class that implements `ITestHostApplication` with convenient lifecycle management:\n\n- **Thread-safe Host Creation** - Lazy initialization with proper locking\n- **Configurable Builder** - Override `CreateBuilderSettings()` to customize host builder settings\n- **Application Configuration** - Override `ConfigureApplication()` to configure services and logging\n- **Async Disposal** - Proper cleanup of host resources\n\n#### Lifecycle Hooks\n\n```csharp\npublic class TestApplication : TestHostApplication\n{\n    // Customize builder settings\n    protected override HostApplicationBuilderSettings? CreateBuilderSettings()\n    {\n        return new HostApplicationBuilderSettings\n        {\n            EnvironmentName = \"Testing\"\n        };\n    }\n\n    // Configure the application\n    protected override void ConfigureApplication(HostApplicationBuilder builder)\n    {\n        base.ConfigureApplication(builder);\n        \n        // Your configuration here\n    }\n\n    // Custom host creation (advanced)\n    protected override IHost CreateHost()\n    {\n        // Custom host creation logic\n        return base.CreateHost();\n    }\n}\n```\n\n## In-Memory Logger\n\nThe in-memory logger captures log messages during test execution for verification and debugging.\n\n### Features\n\n- Capture log entries in memory\n- Query logs by category, log level, or custom filters\n- Thread-safe log collection\n- Configurable capacity and filtering\n- Structured logging support with scopes and state\n\n### Adding the Memory Logger\n\n```csharp\nprotected override void ConfigureApplication(HostApplicationBuilder builder)\n{\n    base.ConfigureApplication(builder);\n    \n    // Add memory logger with default settings\n    builder.Logging.AddMemoryLogger();\n    \n    // Or with custom settings\n    builder.Logging.AddMemoryLogger(options =\u003e\n    {\n        options.MinimumLevel = LogLevel.Debug;\n        options.Capacity = 2048;\n        options.Filter = (category, level) =\u003e category.StartsWith(\"MyApp\");\n    });\n}\n```\n\n### Querying Logs\n\n```csharp\n// Get the memory logger provider\nvar memoryLogger = Application.Services.GetService\u003cMemoryLoggerProvider\u003e();\n\n// Get all logs\nvar allLogs = memoryLogger?.Logs();\n\n// Get logs by category\nvar categoryLogs = memoryLogger?.Logs(\"MyApp.Services.MyService\");\n\n// Get logs by level (warning and above)\nvar warningLogs = memoryLogger?.Logs(LogLevel.Warning);\n\n// Clear logs between tests\nmemoryLogger?.Clear();\n```\n\n### Asserting on Logs\n\n```csharp\n[Test]\npublic async Task VerifyLogging()\n{\n    // Arrange\n    var service = Application.Services.GetRequiredService\u003cIMyService\u003e();\n    var logger = Application.Services.GetService\u003cMemoryLoggerProvider\u003e();\n    \n    // Act\n    service.PerformAction();\n    \n    // Assert\n    var logs = logger?.Logs();\n    \n    await Assert.That(logs).IsNotEmpty();\n    await Assert.That(logs).Contains(log =\u003e \n        log.LogLevel == LogLevel.Information \u0026\u0026\n        log.Message.Contains(\"Action performed\"));\n}\n```\n\n### MemoryLoggerSettings\n\nConfigure the memory logger with these options:\n\n- **`MinimumLevel`** - Minimum log level to capture (default: `LogLevel.Debug`)\n- **`Capacity`** - Maximum number of log entries to keep (default: 1024)\n- **`Filter`** - Custom filter function for fine-grained control\n\n### MemoryLogEntry\n\nLog entries captured include:\n\n- **`Timestamp`** - DateTime when the log entry was created\n- **`LogLevel`** - The log level of the entry (Trace, Debug, Information, Warning, Error, Critical)\n- **`EventId`** - Event identifier associated with the log entry\n- **`Category`** - Category name of the logger that created this entry\n- **`Message`** - Formatted log message\n- **`Exception`** - Exception associated with the log entry, if any (nullable)\n- **`State`** - The state object passed to the logger (nullable)\n- **`Scopes`** - Read-only collection of scope values that were active when the log entry was created\n\n## Integration Testing with Docker Databases\n\nTestHost.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.\n\n### Install Testcontainers\n\n```bash\ndotnet add package Testcontainers.MsSql\n```\n\n### Integration with Test Containers\n\n```csharp\nusing Testcontainers.MsSql;\n\npublic class TestApplication : TestHostApplication, IAsyncInitializer\n{\n    private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder()\n        .WithImage(\"mcr.microsoft.com/mssql/server:2022-latest\")\n        .WithPassword(\"P@ssw0rd123!\")\n        .Build();\n\n    public async Task InitializeAsync()\n    {\n        await _msSqlContainer.StartAsync();\n    }\n\n    protected override void ConfigureApplication(HostApplicationBuilder builder)\n    {\n        base.ConfigureApplication(builder);\n\n        var connectionString = _msSqlContainer.GetConnectionString();\n        builder.Services.AddDbContext\u003cMyDbContext\u003e(options =\u003e\n            options.UseSqlServer(connectionString));\n    }\n\n    public override async ValueTask DisposeAsync()\n    {\n        await _msSqlContainer.DisposeAsync();\n        await base.DisposeAsync();\n    }\n}\n```\n\n### Database Initialization with Hosted Services\n\n```csharp\npublic class DatabaseInitialize : IHostedService\n{\n    private readonly IServiceProvider _serviceProvider;\n\n    public DatabaseInitialize(IServiceProvider serviceProvider)\n    {\n        _serviceProvider = serviceProvider;\n    }\n\n    public async Task StartAsync(CancellationToken cancellationToken)\n    {\n        using var scope = _serviceProvider.CreateScope();\n        var context = scope.ServiceProvider.GetRequiredService\u003cMyDbContext\u003e();\n        \n        await context.Database.EnsureCreatedAsync(cancellationToken);\n        // Seed test data\n    }\n\n    public Task StopAsync(CancellationToken cancellationToken)\n    {\n        return Task.CompletedTask;\n    }\n}\n\n// Register in ConfigureApplication\nbuilder.Services.AddHostedService\u003cDatabaseInitialize\u003e();\n```\n\n### Write Database Tests\n\n```c#\npublic class DatabaseTests\n{\n    [ClassDataSource\u003cTestApplication\u003e(Shared = SharedType.PerAssembly)]\n    public required TestApplication Application { get; init; }\n\n    [Test]\n    public async Task GetUser_WithValidId_ReturnsUser()\n    {\n        // Arrange\n        var dbContext = Application.Services.GetRequiredService\u003cSampleDataContext\u003e();\n\n        // Act\n        var user = await dbContext.Users.FindAsync([1]);\n\n        // Assert\n        await Assert.That(user).IsNotNull();\n        await Assert.That(user.Name).IsEqualTo(\"Test User 1\");\n        await Assert.That(user.Email).IsEqualTo(\"user1@test.com\");\n    }\n\n    [Test]\n    public async Task GetAllUsers_ReturnsSeededUsers()\n    {\n        // Arrange\n        var dbContext = Application.Services.GetRequiredService\u003cSampleDataContext\u003e();\n\n        // Act\n        var users = await dbContext.Users.ToListAsync();\n\n        // Assert\n        await Assert.That(users.Count).IsGreaterThanOrEqualTo(2);\n    }\n}\n```\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n","funding_links":["https://github.com/sponsors/loresoft"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floresoft%2Ftesthost.abstracts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Floresoft%2Ftesthost.abstracts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Floresoft%2Ftesthost.abstracts/lists"}