How to verify ILogger.Log extension method has been called using Moq?

You can’t mock extension methods.

Instead of mocking

logger.LogError(...)

You need to mock the interface method

void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);

LogError actually calls that interface method like this

logger.Log(LogLevel.Error, 0, new FormattedLogValues(message, args), null, (state, ex) => state.ToString());

So you need to mock

 _loggerMock.Verify(logger => logger.Log(It.Is(LogLevel.Error), It.Is(0), It.IsAny<FormattedLogValues>(), It.IsAny<Exception>(), It.IsAny<Func<TState, Exception, string>>()), Times.Once);

Disclaimer I didn’t verify the code

Edit after the comment from pinkfloydx33, I set up a test example in .net50 and came to the following answer

With the most recent framework the FormattedLogValues class has been made internal. So you can’t use this with the generic Moq.It members. But Moq has an alternate way to do this (this answer also mentioned the solution)

For a call to the logger like this

_logger.LogError("myMessage");

You need to verify like this

_loggerMock.Verify(logger => logger.Log(
        It.Is<LogLevel>(logLevel => logLevel == LogLevel.Error),
        It.Is<EventId>(eventId => eventId.Id == 0),
        It.Is<It.IsAnyType>((@object, @type) => @object.ToString() == "myMessage" && @type.Name == "FormattedLogValues"),
        It.IsAny<Exception>(),
        It.IsAny<Func<It.IsAnyType, Exception, string>>()),
    Times.Once);

You use the It.IsAnyType for types where you don’t have access to. And if you need to restrict the verification you can add a func<object, type> to check if the type is what you expect or cast it to the public interface and validate any public members it has.

When you work with a string message and some parameters you need to cast the object of type FormattedLogValues to interface IReadOnlyList<KeyValuePair<string, object?>> and verify the string/values of the different parameters.

Leave a Comment