-
-
Notifications
You must be signed in to change notification settings - Fork 109
feat: enhance test configuration isolation and validation in WebApplicationFactory #4157
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Code ReviewThank you for this PR! I've reviewed the changes to enhance test configuration isolation and validation in WebApplicationFactory. Here's my feedback: ✅ Positive Aspects
🔍 Issues & Concerns1. Breaking Change (High Priority)The signature of
This is a breaking change that will affect any custom implementations or extensions. Since TUnit.AspNetCore is a public API, this should be handled carefully:
2. Potential Null Reference (Medium Priority)In var hostBuilder = base.CreateHostBuilder();
hostBuilder?.ConfigureHostConfiguration(ConfigureStartupConfiguration);
return hostBuilder;While the null-conditional operator is used, the pattern here is fragile. If
3. Duplicated Configuration Logic (Medium Priority)In var configurationBuilder = new ConfigurationManager();
ConfigureStartupConfiguration(configurationBuilder);
configureIsolatedStartupConfiguration(configurationBuilder);
foreach (var keyValuePair in configurationBuilder.AsEnumerable())
{
builder.UseSetting(keyValuePair.Key, keyValuePair.Value);
}This creates a temporary
Recommendation: Document why both mechanisms are needed, or consolidate if possible. 4. Missing XML Documentation (Low Priority)The new
This is especially important since there are now multiple configuration extension points with similar names. 5. Test Coverage (Medium Priority)Based on my review:
However, I don't see explicit unit tests covering:
Recommendation: Add tests to 6. Naming Clarity (Low Priority)The term "startup configuration" may be confusing given ASP.NET Core's historical
📋 Pre-Commit Checklist (per CLAUDE.md)Based on the TUnit development guidelines:
🎯 Recommendations
VerdictThe feature is valuable and the implementation is mostly sound, but needs attention to the breaking change and documentation before merging. I'd recommend addressing the breaking change concern (#1) as a blocker, and the others as nice-to-haves. Great work on improving the configuration isolation! 🚀 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a new ConfigureStartupConfiguration method to enable configuration setup before application startup in WebApplicationFactory, allowing tests to set configuration values that must be available when WebApplication.CreateBuilder(args) is called. The changes demonstrate this capability by adding validation in Program.cs that checks for a configuration value set via the new method.
Key changes:
- Adds
ConfigureStartupConfigurationvirtual method toTestWebApplicationFactoryfor early configuration setup - Modifies
GetIsolatedFactoryto accept startup configuration as a separate parameter from app configuration - Implements
CreateHostBuilderoverride to apply startup configuration viaConfigureHostConfiguration
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| TUnit.AspNetCore/TestWebApplicationFactory.cs | Adds ConfigureStartupConfiguration method and integrates it into both CreateHostBuilder and GetIsolatedFactory for startup-time configuration |
| TUnit.AspNetCore/WebApplicationTest.cs | Updates GetIsolatedFactory call to pass the new startup configuration parameter |
| TUnit.Example.Asp.Net.TestProject/WebApplicationFactory.cs | Implements ConfigureStartupConfiguration to set "SomeKey" configuration value and adds trailing comma to configuration dictionary |
| TUnit.Example.Asp.Net/Program.cs | Adds validation to verify "SomeKey" configuration is set correctly during application startup |
|
|
||
| var builder = WebApplication.CreateBuilder(args); | ||
|
|
||
| if (builder.Configuration["SomeKey"] != "SomeValue") |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This validation check will throw an exception during application startup if the configuration value is not set correctly. While this demonstrates that the startup configuration feature works, throwing an exception in production code for test-specific configuration is problematic.
Consider one of these alternatives:
- Remove this validation after verifying the feature works, as it's test infrastructure code in production code
- Guard it with an environment check:
if (builder.Environment.IsDevelopment() && builder.Configuration["SomeKey"] != "SomeValue") - Move this validation to a dedicated test that verifies the configuration was set correctly, rather than embedding it in Program.cs
The current approach will cause the application to fail to start in any non-test environment where "SomeKey" isn't set to "SomeValue".
| if (builder.Configuration["SomeKey"] != "SomeValue") | |
| if (builder.Environment.IsDevelopment() && | |
| builder.Configuration["SomeKey"] != "SomeValue") |
| builder.ConfigureTestServices(services => services.AddHttpExchangeCapture()); | ||
| } | ||
|
|
||
| configureWebHostBuilder?.Invoke(builder); |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The configureWebHostBuilder escape hatch is now called AFTER the standard configuration (services, configuration, HTTP exchange capture). The PR description mentions "enhance test configuration isolation" but this changes the order compared to the comment on the removed line 29 which said "Apply user's escape hatch configuration first".
This is a subtle behavioral change: previously the escape hatch ran first and could be overridden by standard configuration; now it runs last and can override standard configuration. While the new order may be more useful (allowing tests to override framework defaults), this is a breaking change that should be:
- Documented in the method's XML comments
- Called out in the PR description or release notes
- Verified against existing tests that may rely on the old order
Consider whether this ordering change is intentional and desired.
| testContext, | ||
| _options, | ||
| ConfigureTestServices, | ||
| ConfigureTestConfiguration, |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing ConfigureTestConfiguration here causes it to be applied twice - once as startup configuration (this line) and once as app configuration (line 119). This was introduced when the new configureIsolatedStartupConfiguration parameter was added to GetIsolatedFactory.
The startup configuration parameter should either be an empty lambda or a separate dedicated method. Consider changing line 118 to _ => { } unless there's a specific need for different startup vs app configuration behavior in tests.
| ConfigureTestConfiguration, | |
| _ => { }, |
| // Apply user's escape hatch configuration first | ||
| configureWebHostBuilder?.Invoke(builder); | ||
| var configurationBuilder = new ConfigurationManager(); | ||
| ConfigureStartupConfiguration(configurationBuilder); |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ConfigureStartupConfiguration method is invoked in two different code paths:
- In
CreateHostBuilder(line 63) for the global factory instance - In
GetIsolatedFactory(line 30) for isolated per-test factories
This means that for isolated factories created via GetIsolatedFactory, the startup configuration from ConfigureStartupConfiguration will be applied twice - once when the base factory's host is created, and once when building the isolated factory. This could lead to configuration being duplicated or overridden unexpectedly.
Consider whether ConfigureStartupConfiguration should only be applied in CreateHostBuilder (for the global factory), or if the method in GetIsolatedFactory should skip calling the base ConfigureStartupConfiguration when it's already been applied by the base factory.
| ConfigureStartupConfiguration(configurationBuilder); |
| configureWebHostBuilder?.Invoke(builder); | ||
| }); | ||
| } | ||
|
|
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new ConfigureStartupConfiguration method should have XML documentation comments to explain:
- Its purpose and when it's called
- How it differs from
ConfigureTestConfiguration(in WebApplicationTest) - That it's called during host builder creation, before the application starts
- An example of when to use it versus app configuration
This is especially important since this is part of the public API surface that test authors will extend.
| /// <summary> | |
| /// Configures host-level startup configuration for tests that use this factory. | |
| /// </summary> | |
| /// <remarks> | |
| /// <para> | |
| /// This method is invoked during creation of the generic host builder, before the | |
| /// application startup code runs. Values added to <paramref name="configurationBuilder"/> | |
| /// become part of the host configuration and can be overridden by later configuration | |
| /// sources in the application configuration pipeline. | |
| /// </para> | |
| /// <para> | |
| /// Unlike <c>ConfigureTestConfiguration</c> on <c>WebApplicationTest</c>, which configures | |
| /// the application configuration (for example via | |
| /// <see cref="IWebHostBuilder.ConfigureAppConfiguration(System.Action{WebHostBuilderContext, IConfigurationBuilder})"/>) for a | |
| /// specific test run, this method is intended for host-level or global defaults that | |
| /// apply to all tests using this <see cref="TestWebApplicationFactory{TEntryPoint}"/>. | |
| /// It is called as part of <see cref="IHostBuilder.ConfigureHostConfiguration(System.Action{IConfigurationBuilder})"/>. | |
| /// </para> | |
| /// <para> | |
| /// Use this method when you need to establish baseline configuration for the test host, | |
| /// such as default environment variables, in-memory key/value pairs, or connection strings | |
| /// that should be present for every test. For per-test or scenario-specific configuration, | |
| /// prefer using the <c>configureIsolatedStartupConfiguration</c> or | |
| /// <c>configureIsolatedAppConfiguration</c> delegates passed to | |
| /// <see cref="GetIsolatedFactory(TestContext, WebApplicationTestOptions, System.Action{IServiceCollection}, System.Action{IConfigurationBuilder}, System.Action{WebHostBuilderContext, IConfigurationBuilder}, System.Action{IWebHostBuilder}?)"/> | |
| /// or application-level hooks such as <c>ConfigureTestConfiguration</c>. | |
| /// </para> | |
| /// </remarks> | |
| /// <param name="configurationBuilder"> | |
| /// The <see cref="IConfigurationBuilder"/> used to construct the host configuration | |
| /// before the application starts. | |
| /// </param> |
| { | ||
| // Apply user's escape hatch configuration first | ||
| configureWebHostBuilder?.Invoke(builder); | ||
| var configurationBuilder = new ConfigurationManager(); |
Copilot
AI
Dec 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disposable 'ConfigurationManager' is created but not disposed.
| var configurationBuilder = new ConfigurationManager(); | |
| using var configurationBuilder = new ConfigurationManager(); |
No description provided.