.net core 2.0 ConfigureLogging xunit测试

.net core 2.0 ConfigureLogging xunit test

在我的 .NET Core 2.0 项目的 xUnit 集成测试中,我在终端中看不到日志消息也打印了测试结果。当代码在 WebHost 环境中为 运行 时,日志将打印到控制台。

这是我在测试构造函数中配置日志记录的方式:

var config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.Tests.json")
    .Build();

var services = new ServiceCollection();

services.AddLogging(options => {
    options.AddConfiguration(config.GetSection("Logging"));
    options.AddConsole();
    options.AddDebug();
});

var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("Test");
logger.LogError("From ctor");

但是我没有看到任何日志消息。

xUnit 在版本 2 中发生了变化,不再为测试捕获标准输出:

If you used xUnit.net 1.x, you may have previously been writing output to Console, Debug, or Trace. When xUnit.net v2 shipped with parallelization turned on by default, this output capture mechanism was no longer appropriate; it is impossible to know which of the many tests that could be running in parallel were responsible for writing to those shared resources.

相反,您现在应该使用 an explicit mechanism 写入测试输出。基本上,您不是写入控制台,而是写入一个特殊的 ITestOutputHelper.

当然,ASP.NET 核心日志记录默认不支持这种输出机制。幸运的是,为测试输出编写日志记录提供程序并不太困难。我刚刚实施了一个快速提供程序并将其包含在下面的回答中。你可以这样使用它:

public class Example
{
    private readonly ILogger<Example> _logger;

    public Example(ITestOutputHelper testOutputHelper)
    {
        var loggerFactory = LoggerFactory.Create(l =>
        {
            l.AddProvider(new XunitLoggerProvider(testOutputHelper));
        });
        _logger = loggerFactory.CreateLogger<Example>();
    }

    [Fact]
    public void Test()
    {
        _logger.LogDebug("Foo bar baz");
    }
}

请注意,通常应避免在单元测试中创建完整的依赖注入容器。在单元测试中使用 DI 通常表明您的单元测试不是 unit 测试,而是集成测试。在单元测试中,您应该只测试一个特定的单元并显式地传递它的所有依赖项——通常只是作为一个模拟。正如你在上面的例子中看到的,创建一个记录器实际上是一件非常简单的事情,不需要 DI。


正如所承诺的,这是 XunitLoggerProviderXunitLogger,您需要 运行 上面显示的代码,并将 Microsoft.Extensions.Logging 框架与 xUnit 测试集成输出:

public class XunitLoggerProvider : ILoggerProvider
{
    private readonly ITestOutputHelper _testOutputHelper;

    public XunitLoggerProvider(ITestOutputHelper testOutputHelper)
    {
        _testOutputHelper = testOutputHelper;
    }

    public ILogger CreateLogger(string categoryName)
        => new XunitLogger(_testOutputHelper, categoryName);

    public void Dispose()
    { }
}

public class XunitLogger : ILogger
{
    private readonly ITestOutputHelper _testOutputHelper;
    private readonly string _categoryName;

    public XunitLogger(ITestOutputHelper testOutputHelper, string categoryName)
    {
        _testOutputHelper = testOutputHelper;
        _categoryName = categoryName;
    }

    public IDisposable BeginScope<TState>(TState state)
        => NoopDisposable.Instance;

    public bool IsEnabled(LogLevel logLevel)
        => true;

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        _testOutputHelper.WriteLine($"{_categoryName} [{eventId}] {formatter(state, exception)}");
        if (exception != null)
            _testOutputHelper.WriteLine(exception.ToString());
    }

    private class NoopDisposable : IDisposable
    {
        public static readonly NoopDisposable Instance = new NoopDisposable();
        public void Dispose()
        { }
    }
}