You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用本地.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 11:54:10