From 24f47cab4d1c7f5614672f4e438d844d3651d5a1 Mon Sep 17 00:00:00 2001 From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com> Date: Wed, 24 Dec 2025 11:44:24 +0000 Subject: [PATCH] feat: enhance test configuration isolation and validation in WebApplicationFactory --- TUnit.AspNetCore/TestWebApplicationFactory.cs | 37 +++++++++++++++---- TUnit.AspNetCore/WebApplicationTest.cs | 1 + .../WebApplicationFactory.cs | 10 ++++- TUnit.Example.Asp.Net/Program.cs | 5 +++ 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/TUnit.AspNetCore/TestWebApplicationFactory.cs b/TUnit.AspNetCore/TestWebApplicationFactory.cs index f489c846ea..2cb6701eb6 100644 --- a/TUnit.AspNetCore/TestWebApplicationFactory.cs +++ b/TUnit.AspNetCore/TestWebApplicationFactory.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using TUnit.AspNetCore.Extensions; using TUnit.AspNetCore.Interception; using TUnit.Core; @@ -14,24 +15,31 @@ namespace TUnit.AspNetCore; /// public abstract class TestWebApplicationFactory : WebApplicationFactory where TEntryPoint : class { + public WebApplicationFactory GetIsolatedFactory( TestContext testContext, WebApplicationTestOptions options, - Action configureServices, - Action configureConfiguration, + Action configureIsolatedServices, + Action configureIsolatedStartupConfiguration, + Action configureIsolatedAppConfiguration, Action? configureWebHostBuilder = null) { return WithWebHostBuilder(builder => { - // Apply user's escape hatch configuration first - configureWebHostBuilder?.Invoke(builder); + var configurationBuilder = new ConfigurationManager(); + ConfigureStartupConfiguration(configurationBuilder); + configureIsolatedStartupConfiguration(configurationBuilder); + + foreach (var keyValuePair in configurationBuilder.AsEnumerable()) + { + builder.UseSetting(keyValuePair.Key, keyValuePair.Value); + } - // Then apply standard configuration builder - .ConfigureAppConfiguration(configureConfiguration) + .ConfigureAppConfiguration(configureIsolatedAppConfiguration) .ConfigureTestServices(services => { - configureServices(services); + configureIsolatedServices(services); services.AddSingleton(testContext); }); @@ -39,6 +47,21 @@ public WebApplicationFactory GetIsolatedFactory( { builder.ConfigureTestServices(services => services.AddHttpExchangeCapture()); } + + configureWebHostBuilder?.Invoke(builder); }); } + + protected virtual void ConfigureStartupConfiguration(IConfigurationBuilder configurationBuilder) + { + } + + protected override IHostBuilder? CreateHostBuilder() + { + var hostBuilder = base.CreateHostBuilder(); + + hostBuilder?.ConfigureHostConfiguration(ConfigureStartupConfiguration); + + return hostBuilder; + } } diff --git a/TUnit.AspNetCore/WebApplicationTest.cs b/TUnit.AspNetCore/WebApplicationTest.cs index 1de04a34d5..00a4793fb9 100644 --- a/TUnit.AspNetCore/WebApplicationTest.cs +++ b/TUnit.AspNetCore/WebApplicationTest.cs @@ -115,6 +115,7 @@ public async Task InitializeFactoryAsync(TestContext testContext) testContext, _options, ConfigureTestServices, + ConfigureTestConfiguration, (_, config) => ConfigureTestConfiguration(config), ConfigureWebHostBuilder); diff --git a/TUnit.Example.Asp.Net.TestProject/WebApplicationFactory.cs b/TUnit.Example.Asp.Net.TestProject/WebApplicationFactory.cs index 63ae7cf69c..1342148176 100644 --- a/TUnit.Example.Asp.Net.TestProject/WebApplicationFactory.cs +++ b/TUnit.Example.Asp.Net.TestProject/WebApplicationFactory.cs @@ -49,8 +49,16 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) { { "Database:ConnectionString", PostgreSql.Container.GetConnectionString() }, { "Redis:ConnectionString", Redis.Container.GetConnectionString() }, - { "Kafka:ConnectionString", Kafka.Container.GetBootstrapAddress() } + { "Kafka:ConnectionString", Kafka.Container.GetBootstrapAddress() }, }); }); } + + protected override void ConfigureStartupConfiguration(IConfigurationBuilder configurationBuilder) + { + configurationBuilder.AddInMemoryCollection(new Dictionary + { + { "SomeKey", "SomeValue" } + }); + } } diff --git a/TUnit.Example.Asp.Net/Program.cs b/TUnit.Example.Asp.Net/Program.cs index d4663de0bb..1ba58798ea 100644 --- a/TUnit.Example.Asp.Net/Program.cs +++ b/TUnit.Example.Asp.Net/Program.cs @@ -5,6 +5,11 @@ var builder = WebApplication.CreateBuilder(args); +if (builder.Configuration["SomeKey"] != "SomeValue") +{ + throw new InvalidOperationException("SomeKey is not SomeValue - But we set it in WebApplicationFactory"); +} + builder.Services.AddOpenApi(); builder.Services.Configure(builder.Configuration.GetSection(DatabaseOptions.SectionName)); builder.Services.Configure(builder.Configuration.GetSection(RedisOptions.SectionName));