Log.cs
public static partial class Log
{
[LoggerMessage(EventId = 1000, Level = LogLevel.Information, Message = "{Message}")]
public static partial void OutputMessage(this ILogger logger, string message);
}
MyService.cs
public class MyService(ILogger<Service> logger) : IMyService
{
private readonly ILogger<Service> _logger = logger;
public void DoSomething()
{
_logger.OutputMessage("DoSomething");
}
}
こんな感じのコードを作って、ログを出力しているかどうかのテストを作ると
MyServiceTests.cs
public class MyServiceTests
{
[Fact]
public void DoSomethingTest()
{
var logger = new Mock<ILogger<Service>>();
var service = new MyService(logger);
service.DoSomething();
logger.Verify(logger => logger.Log(It.Is<LogLevel>(loglevel => loglevel == LogLevel.Information),
It.Is<EventId>(eventId => eventId.Id == 1000),
It.Is<It.IsAnyType>(o, t) => true),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
Times.Once);
}
}
こんな感じのコードになる。Copilotに作らせても同じようなコードになる。
が、実行すると例外を吐いて止まってしまう。
Performed invocations:
Mock:1> (e):
ILogger.IsEnabled(LogLevel.Information)
ILogger.IsEnabled(LogLevel)
って何よってことで、デバッカで掘っていくと
LoggerMessage.g.cs
if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Information))
{
__OutputMessageCallback(logger, message, null);
}
こういうコードに突き当たる。
というわけで、
MyServiceTests.cs
public class MyServiceTests
{
[Fact]
public void DoSomethingTest()
{
var logger = new Mock<ILogger<Service>>();
logger.Setup(logger => logger.IsEnabled(LogLevel.Information)).Returns(true);
var service = new MyService(logger);
service.DoSomething();
logger.Verify(logger => logger.Log(It.Is<LogLevel>(loglevel => loglevel == LogLevel.Information),
It.Is<EventId>(eventId => eventId.Id == 1000),
It.Is<It.IsAnyType>(o, t) => true),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)),
Times.Once);
}
}
こうすることでテストが完走するようになりました、という実体験を文字起こし。