调用本地.NET API的集成测试如何获取详细运行日志
解决.NET Core集成测试中API日志输出到测试标准输出的问题
问题原因
你配置的AddConsole日志提供程序默认输出到集成测试宿主进程的独立标准输出流,测试运行器默认不会捕获该流的内容;同时你示例中直接调用Console.WriteLine的输出也不会默认关联到测试用例的输出上下文,这就是你看不到日志的核心原因。
实现方案
无需修改原有API代码,也无需修改测试业务逻辑,仅需调整测试项目中的WebApplicationFactory实现并增加少量辅助类即可:
步骤1:修改TodoApplication实现
using System; using System.IO; using System.Text; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Xunit.Abstractions; namespace Sample.Tests { class TodoApplication : WebApplicationFactory<Program> { // 测试输出实例,由测试用例注入 public ITestOutputHelper? TestOutput { get; set; } protected override IHost CreateHost(IHostBuilder builder) { var originalConsoleOut = Console.Out; // 配置日志转发 builder.ConfigureLogging(logging => { // 清除默认日志提供程序,避免配置覆盖 logging.ClearProviders(); // 按需调整最低日志输出级别 logging.SetMinimumLevel(LogLevel.Debug); if (TestOutput != null) { logging.AddProvider(new TestOutputLoggerProvider(TestOutput)); } }); // 重定向Console.WriteLine输出到测试输出 builder.ConfigureServices(services => { if (TestOutput != null) { Console.SetOut(new TestOutputWriter(TestOutput)); } }); var host = base.CreateHost(builder); // 宿主销毁时恢复标准输出,避免影响其他测试 host.Services.GetRequiredService<IHostApplicationLifetime>() .ApplicationStopped.Register(() => Console.SetOut(originalConsoleOut)); return host; } } // 辅助类:转发Console输出到测试输出 internal class TestOutputWriter : TextWriter { private readonly ITestOutputHelper _output; public override Encoding Encoding => Encoding.UTF8; public TestOutputWriter(ITestOutputHelper output) => _output = output; public override void WriteLine(string? value) { if (!string.IsNullOrWhiteSpace(value)) { _output.WriteLine(value); } } } // 辅助类:日志提供程序,转发ILogger输出到测试输出 internal class TestOutputLoggerProvider : ILoggerProvider { private readonly ITestOutputHelper _output; public TestOutputLoggerProvider(ITestOutputHelper output) => _output = output; public ILogger CreateLogger(string categoryName) => new TestOutputLogger(categoryName, _output); public void Dispose() { } } // 辅助类:日志实现 internal class TestOutputLogger : ILogger { private readonly string _categoryName; private readonly ITestOutputHelper _output; public TestOutputLogger(string categoryName, ITestOutputHelper output) { _categoryName = categoryName; _output = output; } public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null; public bool IsEnabled(LogLevel logLevel) => true; public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) { var message = formatter(state, exception); if (exception != null) { message += $"{Environment.NewLine}{exception}"; } _output.WriteLine($"[{logLevel}] {_categoryName}: {message}"); } } }
步骤2:测试类注入测试输出实例
仅需要在测试类的构造函数中注入测试框架提供的ITestOutputHelper,赋值给TodoApplication即可,原有测试业务逻辑完全不需要修改:
using Xunit; using Xunit.Abstractions; namespace Sample.Tests { public class TodoApiTests { private readonly ITestOutputHelper _outputHelper; // Xunit自动注入ITestOutputHelper实例 public TodoApiTests(ITestOutputHelper outputHelper) { _outputHelper = outputHelper; } [Fact] public async Task GetTodos() { await using var application = new TodoApplication { // 注入测试输出实例 TestOutput = _outputHelper }; var client = application.CreateClient(); // 原有测试业务逻辑完全不变 var todos = await client.GetFromJsonAsync<List<string>>("/todos"); Assert.Empty(todos); } } }
效果验证
运行测试后,在测试结果的输出面板中即可看到示例中的Something is going wrong.日志,同时所有API通过ILogger打印的日志、异常堆栈信息也会一并输出到测试结果中。
内容的提问来源于stack exchange,提问作者user13265595
相关产品推荐
相关产品推荐

