verify logs with Moq

To assert that logs were generated when writing C# unit tests with Moq:

public static void VerifyLog<T>(this Mock<ILogger<T>> mock, string logMessage, LogLevel level = LogLevel.Error, int times = 1)
    where T : class
{
    mock.Verify(l =>
        l.Log(
            level,
            It.IsAny<EventId>(),
            It.Is<It.IsAnyType>((o, t) =>
                o.ToString().Contains(logMessage, StringComparison.InvariantCultureIgnoreCase)),
            It.IsAny<Exception>(),
            (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
        Times.Exactly(times)
    );
}

public static void VerifyLog<T, TEx>(this Mock<ILogger<T>> mock, string logMessage, LogLevel level = LogLevel.Error, int times = 1)
    where T : class
    where TEx : Exception
{
    mock.Verify(l =>
        l.Log(
            level,
            It.IsAny<EventId>(),
            It.Is<It.IsAnyType>((o, t) =>
                o.ToString().Contains(logMessage, StringComparison.InvariantCultureIgnoreCase)),
            It.IsAny<TEx>(),
            (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
        Times.Exactly(times)
    );
}

public static void VerifyLog<T>(this Mock<ILogger<T>> mock, string logMessage, LogLevel level, Times times)
    where T : class
{
    mock.Verify(l =>
            l.Log(
                level,
                It.IsAny<EventId>(),
                It.Is<It.IsAnyType>((o, t) =>
                    o.ToString().Contains(logMessage, StringComparison.InvariantCultureIgnoreCase)),
                It.IsAny<Exception>(),
                (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),
        times
    );
}

Then

Mock<ILogger<Whatever>> logger = new();

doSomething();

logger.VerifyLog("something was done", LogLevel.Warning);
// or
logger.VerifyLog<ArgumentException>("something that logged an exception", LogLevel.Debug);