web-dev-qa-db-ja.com

.netコア2.0 ConfigureLogging xunitテスト

.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");

しかし、ログメッセージは表示されません。

11
Max

xUnitはバージョン2で変更され、テストの標準出力をキャプチャしなくなりました。

XUnit.net 1.xを使用した場合は、以前にConsoleDebug、またはTraceに出力を書き込んでいた可能性があります。 xUnit.net v2がデフォルトで並列化をオンにして出荷されたとき、この出力キャプチャメカニズムは適切ではなくなりました。並行して実行できる多くのテストのどれがそれらの共有リソースへの書き込みの原因であったかを知ることは不可能です。

代わりに、テスト出力に書き込むために 明示的なメカニズム を使用することになっています。基本的に、コンソールに書き込む代わりに、特別なITestOutputHelperに書き込みます。

もちろん、この出力メカニズムはASP.NET Coreロギングではデフォルトでサポートされていません。幸い、テスト出力用のロギングプロバイダーを作成することはそれほど難しくありません。クイックプロバイダーを実装し、以下の回答に含めました。次のように使用できます。

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

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

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

通常、単体テストでは完全な依存関係注入コンテナーを作成することは避けてください。単体テストにDIがあることは、通常、単体テストがunitテストではなく、統合テストであることを示しています。ユニットテストでは、特定のユニットを1つだけテストし、そのすべての依存関係を明示的に渡す必要があります。モックとしてだけでなく、上記の例でわかるように、ロガーを作成することは、実際には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 NoopDisposable Instance = new NoopDisposable();
        public void Dispose()
        { }
    }
}
21
poke