.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で変更され、テストの標準出力をキャプチャしなくなりました。
XUnit.net 1.xを使用した場合は、以前に
Console
、Debug
、または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なしで実行する非常に単純なことです。
約束どおり、これはXunitLoggerProvider
とXunitLogger
であり、上記のコードを実行し、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()
{ }
}
}