やりたいこと
- xUnit による単体テストの中で
ILogger<T>
が注入されるクラスをテストする - 記録される内容を確認できるようにする
環境
- Microsoft Visual Studio Community 2022 Version 17.9.7
- .NET8 C# 12
テスト対象
using Microsoft.Extensions.Logging;
namespace HigeDaruma.DemoXUnit.ClassLibrary.Messages;
public sealed class MessageService(ILogger<MessageService> logger)
{
public string GetMessage(string name)
{
logger.LogInformation("Method:{Method},Name:{Name}", "MessageService.GetMessage(string)", name);
return $"Hello, World {name} !";
}
}
プライマリーコンストラクターに注入された ILogger<T>
を使ってロギングしています。このクラスをテストできるようにし、LogInformation
の内容を確認できるようにします。
手順
まずは ITestOutputHelper をつかってみる
xUnit で単体テストを実装した場合、テストエクスプローラーにログを表示するには ITestOutputHelper
を活用します。
using Xunit.Abstractions;
namespace HigeDaruma.DemoXUnit.ClassLibrary.UnitTest.Messages;
public sealed class MessageServiceTest
{
private readonly ITestOutputHelper _testOutputHelper;
public MessageServiceTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
[Fact]
public void TestOutputHelperTest()
{
string name = "HigeDaruma";
_testOutputHelper.WriteLine(name);
}
}
個のテストを実行すると、テストエクスプローラーの「テスト詳細の概要」(どっちやねん)の「標準出力」の領域に WriteLine()
した内容が表示されています。
この ITestOutputHelper
を ILogger<T>
として注入できるようにすれば、やりたいことを実現できそうです。
xUnit プロジェクトに NuGet パッケージを追加する
-
Microsoft.Extensions.Logging
を追加します
Logger/LoggerProvider を実装する
-
ILogger
を実装するXunitLogger
を追加します
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;
namespace HigeDaruma.DemoXUnit.ClassLibrary.UnitTest;
internal sealed class XunitLogger(
ITestOutputHelper testOutputHelper,
string categoryName) : ILogger
{
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
{
return NopDisposable.Instance;
}
public bool IsEnabled(LogLevel logLevel)
{
return 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 is not null)
{
testOutputHelper.WriteLine(exception.ToString());
}
}
private class NopDisposable : IDisposable
{
public static NopDisposable Instance = new();
public void Dispose()
{ }
}
}
2. ILoggerProvider
を実装する XunitLoggerProvider
を追加する
using Microsoft.Extensions.Logging;
using Xunit.Abstractions;
namespace HigeDaruma.DemoXUnit.ClassLibrary.UnitTest;
internal sealed class XunitLoggerProvider(
ITestOutputHelper testOutputHelper) : ILoggerProvider
{
public ILogger CreateLogger(string categoryName)
{
return new XunitLogger(testOutputHelper, categoryName);
}
public void Dispose()
{
}
}
3. XunitLogger
を ILogger<T>
として注入できるように準備
using Xunit.Abstractions;
namespace HigeDaruma.DemoXUnit.ClassLibrary.UnitTest.Messages;
public sealed class MessageServiceTest
{
private readonly ITestOutputHelper _testOutputHelper;
+
+ private readonly ILogger<MessageService> _logger;
public MessageServiceTest(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
+
+ LoggerFactory loggerFactory = new();
+ loggerFactory.AddProvider(new XunitLoggerProvider(testOutputHelper));
+ _logger = loggerFactory.CreateLogger<MessageService>();
}
[Fact]
public void TestOutputHelperTest()
{
string name = "HigeDaruma";
_testOutputHelper.WriteLine(name);
}
}
4. テストメソッドで使用する
[Fact]
public void GetMessage_ReturnsOk_WhenInputNameIsOk()
{
// Arrange
string name = "HigeDaruma";
MessageService instance = new(_logger);
// Act
string result = instance.GetMessage(name);
// Assert
Assert.Equal("Hello, World HigeDaruma !", result);
}
MessageService
のコンストラクターに _logger
を注入し、GetMessage(string)
を実行します。インスタンスを生成できるだけではなく、テストエクスプローラーにもログ内容が表示されます。
まとめ
xUnit でデバッグに使える ITestOutputHelper
を使って ILogger<T>
を実現しました。これで単体テストがはかどります。
参考