使用WebApplicationFactory测试配置Serilog的Web API控制器时,解决_apiConfiguration空引用异常的方法
WebApplicationFactory测试配置Serilog的Web API控制器时,解决_apiConfiguration空引用异常的方法
你遇到的问题我太熟悉了——WebApplicationFactory做集成测试时确实会跳过Program的Main方法,直接调用CreateHostBuilder,这就导致你在Main里初始化的_apiConfiguration静态变量完全没机会赋值,自然在配置Serilog的时候就炸空引用了。
下面给你两个实用的修复方案,你可以根据自己的需求选:
方案一:重构原项目代码,让配置加载逻辑内置到CreateHostBuilder里
最彻底的解决方式是把原本放在Main里的配置加载逻辑移到CreateHostBuilder内部,这样不管是正常启动还是测试启动,配置都会被正确初始化,再也不依赖外部静态变量:
修改后的Program.cs代码:
public class Program { public static async Task Main(string[] args) { try { await CreateHostBuilder(args).Build().RunAsync(); } catch (Exception ex) { // 这里处理异常日志 } } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { // 把Main里的配置加载逻辑移到这里 var environment = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); if (environment.IsDevelopment()) { config.AddJsonFile($"appsettings.{environment.EnvironmentName}.json", optional: true, reloadOnChange: true); } config.AddEnvironmentVariables(); }) .ConfigureLogging((hostingContext, loggingBuilder) => { // 直接用hostingContext里的配置,再也不用怕null了 Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(hostingContext.Configuration) .Enrich.FromLogContext() .Enrich.WithProperty("ApplicationName", "WeatherAPI") .WriteToOtlpSerilog() .CreateLogger(); loggingBuilder.AddSerilog(); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
这种写法的好处是,生产代码和测试代码的启动逻辑完全统一,不会出现测试和生产行为不一致的情况。
方案二:在测试项目中重写WebApplicationFactory,覆盖配置逻辑
如果你不想改动生产代码,也可以在测试层直接覆盖配置和日志逻辑,绕开原项目的静态变量依赖:
[TestClass] public class WeatherControllerTests : WebApplicationFactory<Program> { protected override void ConfigureWebHost(IWebHostBuilder builder) { // 第一步:给测试环境加载专属配置 builder.ConfigureAppConfiguration(config => { var testConfig = new ConfigurationBuilder() .AddJsonFile("appsettings.Test.json", optional: false) .Build(); config.AddConfiguration(testConfig); }); // 第二步:重新配置Serilog,不依赖原项目的_apiConfiguration builder.ConfigureLogging(loggingBuilder => { Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .Enrich.FromLogContext() .Enrich.WithProperty("ApplicationName", "WeatherAPI") // 测试时可以用控制台输出代替OTLP,避免依赖外部服务 .WriteTo.Console() .CreateLogger(); loggingBuilder.AddSerilog(); }); } [TestMethod] public async Task TestAsync() { var client = CreateClient(); var response = await client.GetAsync("/weather/getforecast"); Assert.IsNotNull(response); } }
这种方式适合不想动生产代码的场景,直接在测试层把有问题的逻辑覆盖掉,快速解决测试报错的问题。
再啰嗦一句问题根源
WebApplicationFactory的设计初衷就是直接构建测试宿主,跳过Main方法里的自定义启动逻辑(比如你写的异常捕获、手动配置加载)。你原本把配置初始化放在Main里,测试时这部分代码根本没机会跑,_apiConfiguration自然就是null,空引用异常也就不可避免了。
不管选哪种方案,核心都是让配置在CreateHostBuilder的执行流程中被初始化,而不是依赖只有正常启动才会执行的Main方法。
内容来源于stack exchange
相关产品推荐
相关产品推荐

