Course contentsShow
← C#
Lesson 1648 of 1,90815. Software Quality and TestingFree lesson

Mocking ILogger

Verify that logging statements are recorded without needing a full logging backend.

In this lesson, you will learn how to verify that your business logic correctly records diagnostic information using ILogger without needing to configure a real logging backend.

The Challenge with ILogger

When testing classes that depend on Microsoft.Extensions.Logging.ILogger<T>, you face a unique hurdle: the Log method is an extension method. Because Moq and other mocking frameworks cannot intercept static extension methods, you cannot directly verify calls like logger.LogInformation(...) using standard setup syntax.

To overcome this, we target the underlying interface method that the extension methods call: Log<TState>.

The Implementation Strategy

Since Log<TState> uses generic arguments and complex state structures, verifying it directly is often messy. The "Advanced" approach is to create a helper method or use an argument matcher to inspect the internal state.

Consider this service:

public class OrderService {
    private readonly ILogger<OrderService> _logger;
    public OrderService(ILogger<OrderService> logger) => _logger = logger;

    public void Process(int id) {
        _logger.LogInformation("Processing order {Id}", id);
    }
}

To verify this in your test, you must match the signature of the Log method. Since Log is internal to the logging infrastructure, we verify that it was called with specific parameters:

[Fact]
public void Process_LogsInformation() {
    var mockLogger = new Mock<ILogger<OrderService>>();

    var service = new OrderService(mockLogger.Object);
    service.Process(123);

    mockLogger.Verify(
        l => l.Log(
            LogLevel.Information,
            It.IsAny<EventId>(),
            It.Is<It.IsAnyType>((v, t) => v.ToString().Contains("Processing order 123")),
            null,
            It.IsAny<Func<It.IsAnyType, Exception, string>>()),
        Times.Once);
}

Why this works

By using It.IsAnyType, we satisfy the generic requirement of the Log method. The second argument in our It.Is<...> predicate allows us to inspect the state object, which is where the message template and the formatted string reside. This allows you to verify that your service is communicating correctly with the logging abstraction while keeping your tests isolated from the file system, console, or external log providers.

Key Takeaway: To mock ILogger, verify the underlying Log method using It.IsAnyType to intercept the generic state passed by extension methods.