DEV Community

Jack Lin
Jack Lin

Posted on

Unit Testing ILogger with NUnit and Moq

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!");
    }
}
Enter fullscreen mode Exit fullscreen mode

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]));
    }
}
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
henryjs profile image
James

Why are you unit testing the logger? Isn't that a little redundant?

Collapse
 
blueskyson profile image
Jack Lin • Edited

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.