.NET 8中如何对AspNetCore应用启动进行单元测试?
.NET 8 中复用应用启动配置进行DI验证单元测试
之前在.NET 6里,我通过WebHost Builder结合Startup类做API的单元测试,验证配置和依赖注入(DI)的正确性。升级到.NET 8后,采用了program.cs的简洁写法,原来的单元测试失效了。我想只在一处维护应用启动逻辑,让单元测试直接复用这套配置。
旧.NET 6启动方式
public static IHostBuilder CreateHostForDependencyInjectionTests(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); if (args != null) { config.AddCommandLine(args); } }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) // Add a new service provider configuration .UseDefaultServiceProvider((context, options) => { options.ValidateScopes = true; options.ValidateOnBuild = true; });
原单元测试代码
[TestCase("Development")] [TestCase("Production")] public void DependencyRegistrationIsValid(string env) { Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", env); var hostBuilder = WebApiHostBuilder.CreateHostForDependencyInjectionTests(new string[0]); var host = hostBuilder.Build(); Assert.NotNull(host); }
升级后的问题
升级到.NET 8后不能再用Startup类,尝试往builder.WebHost里加相关配置会触发ASP0010错误。
我的新.NET 8 program.cs代码:
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.ConfigureSwagger(); var app = builder.Build(); // Configure the HTTP request pipeline. app.UseSwagger(); app.UseSwaggerUI(); app.UseCustomExceptionHandler(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
我希望能在单元测试里启动API应用,让所有配置或DI的错误直接抛出来,方便修复。
解决方案
要实现一处维护启动逻辑、单元测试复用的目标,可以把program.cs里的配置逻辑抽成可复用的方法,同时避免ASP0010错误。
步骤1:重构program.cs,抽离配置逻辑
把服务注册和中间件配置拆成单独的扩展方法,让单元测试可以直接调用:
var builder = WebApplication.CreateBuilder(args); // 复用抽离的服务注册逻辑 builder.ConfigureServices(); var app = builder.Build(); // 复用抽离的中间件配置逻辑 app.ConfigurePipeline(); app.Run(); // 扩展方法可放在同一命名空间或单独类库中 public static class ApplicationConfiguration { public static WebApplicationBuilder ConfigureServices(this WebApplicationBuilder builder) { builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.ConfigureSwagger(); // 保留DI验证配置 builder.Host.UseDefaultServiceProvider((context, options) => { options.ValidateScopes = true; options.ValidateOnBuild = true; }); // 复用原配置加载逻辑 builder.Configuration.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); if (builder.Args != null) { builder.Configuration.AddCommandLine(builder.Args); } return builder; } public static WebApplication ConfigurePipeline(this WebApplication app) { // 环境判断保留原逻辑 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseCustomExceptionHandler(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); return app; } }
步骤2:创建可复用的测试用应用构建方法
适配.NET 8的WebApplication模型,构建应用但不启动HTTP服务器:
public static class WebApiHostBuilder { public static WebApplication CreateApplicationForTests(string[] args) { var builder = WebApplication.CreateBuilder(args); // 复用program.cs中的服务配置 builder.ConfigureServices(); // 构建应用但不启动 var app = builder.Build(); // 复用中间件配置 app.ConfigurePipeline(); return app; } }
步骤3:修改单元测试代码
直接调用新的构建方法,验证DI和配置的正确性:
[TestCase("Development")] [TestCase("Production")] public void DependencyRegistrationIsValid(string env) { Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", env); var app = WebApiHostBuilder.CreateApplicationForTests(new string[0]); Assert.NotNull(app); // 可选:验证特定服务是否正确注册 var serviceProvider = app.Services; var controllerActivator = serviceProvider.GetService<IControllerActivator>(); Assert.NotNull(controllerActivator); }
关键说明
- 抽离扩展方法后,program.cs和单元测试共用同一套配置逻辑,无需重复维护。
- 构建应用时不调用
Run(),避免启动HTTP服务器,专注于DI和配置验证。 - 保留
UseDefaultServiceProvider的验证配置,确保DI注册错误在构建阶段就抛出,提前发现问题。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

