xUnit 在版本 2 中已更改为不再捕获测试的标准输出:
如果您使用 xUnit.net 1.x,您之前可能已经将输出写入 Console、Debug 或 Trace。当 xUnit.net v2 默认开启并行化时,这种输出捕获机制不再适用;不可能知道可以并行运行的许多测试中的哪一个负责写入这些共享资源。
相反,您现在应该使用an explicit mechanism 写入测试输出。基本上,您不是写到控制台,而是写到一个特殊的ITestOutputHelper。
当然,ASP.NET Core 日志默认不支持这种输出机制。幸运的是,为测试输出编写日志提供程序并不太难。我刚刚实现了一个快速提供程序,并将其包含在下面的答案中。你可以这样使用它:
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。
正如所承诺的,这是运行上面显示的代码并将Microsoft.Extensions.Logging 框架与xUnit 测试输出集成所需的XunitLoggerProvider 和XunitLogger:
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()
{ }
}
}