Environment: .NET 6.0
Create a console application, then create Foo.cs
and fill in it with the followin code:
using Microsoft.Extensions.Logging;
namespace MyLog;
public class Foo
{
private ILogger<Foo> _logger;
public Foo(ILogger<Foo> logger)
{
_logger = logger;
}
public void PrintMessage()
{
_logger.LogInformation("Hello World!");
}
}
Create an NUnit project and create a UnitTest.cs
with the following content:
using Moq;
using Microsoft.Extensions.Logging;
using NUnit.Framework;
using MyLog;
namespace MyTest;
public class Tests
{
[Test]
public void Test1()
{
List<string> logMessages = new List<string>();
var mockLogger = new Mock<ILogger<Foo>>();
mockLogger.Setup(m => m.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()!
)).Callback(new InvocationAction(invocation => {
var logLevel = (LogLevel)invocation.Arguments[0];
var eventId = (EventId)invocation.Arguments[1];
var state = invocation.Arguments[2];
var exception = (Exception)invocation.Arguments[3];
var formatter = invocation.Arguments[4];
var invokeMethod = formatter.GetType().GetMethod("Invoke");
var logMessage = invokeMethod!.Invoke(formatter, new[] { state, exception });
logMessages.Add((string)logMessage!);
}));
Foo foo = new Foo(mockLogger.Object);
foo.PrintMessage();
Assert.That("Hello World!", Is.EqualTo(logMessages[0]));
}
}
Finally, execute the test and it will pass successfully. All logs will be stored in logMessages
, you can manipulate it for unit testing.
Top comments (2)
Why are you unit testing the logger? Isn't that a little redundant?
Yes, my colleagues suggested that counting the calling times of a specific function is better, so I didn't mock ILogger any more. But I think this technique is still worth writing down.