ILogger 已经成了 Asp.net core 和 azure function里面默认的Logger了。
但是它的一些调用方法是扩展方法 是放在 Microsoft.Extensions.Logging.LoggerExtensions 里面的。
对于扩展方法我们是没有办法直接Mock的。
所以我们得去Mock它的
Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
方法
using Microsoft.Extensions.Logging;
using Moq;
using System;
using System.Collections.Generic;
namespace Xunit
{
public class LogMockInfo
{
public LogLevel Level { get; set; }
public EventId EventId { get; set; }
public IReadOnlyCollection<KeyValuePair<string, object>> State { get; set; }
public Exception Exception { get; set; }
}
public static class LogMockHelper
{
public static Mock<ILogger> Get(LogMockInfo info)
{
var msLogger = new Mock<Microsoft.Extensions.Logging.ILogger>();
msLogger.Setup(x => x.IsEnabled(It.IsAny<LogLevel>())).Returns(true)
.Callback<LogLevel>(x =>
{
});
msLogger.Setup(it => it.Log<It.IsAnyType>(It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()))
.Callback((IInvocation invocation) =>
{
info.Level = (LogLevel)invocation.Arguments[0];
info.EventId = (EventId)invocation.Arguments[1];
info.State = (IReadOnlyCollection<KeyValuePair<string, object>>)invocation.Arguments[2];
info.Exception = invocation.Arguments[3] as Exception;
var formatter = invocation.Arguments[4] as Delegate;
var formatterStr = formatter.DynamicInvoke(info.State, info.Exception);
});
return msLogger;
}
}
}
测试代码
using Microsoft.Extensions.Logging;
using System;
using Xunit;
namespace MyFirstUnitTests
{
public class LogTest
{
[Fact]
public void Log_Test_Example()
{
var logInfo = new LogMockInfo();
var msLogger = LogMockHelper.Get(logInfo);
var exception = new FormatException();
msLogger.Object.IsEnabled(LogLevel.Debug);
msLogger.Object.LogError(exception, "abc");
Assert.Equal(LogLevel.Error, logInfo.Level);
Assert.Equal(exception, (FormatException)logInfo.Exception);
}
}
}
我们这个LogMockHelper支持的版本是5.0.0,旧版本或者新版本,泛型的类型可能不一样。
5.0.0的调用方式是 logger.Log<FormattedLogValues>(logLevel, eventId, new FormattedLogValues(message, args), exception, LoggerExtensions._messageFormatter);
然后这个FormattedLogValues 是internal级别的。
我们访问不到它。
还好是Moq有提供了一个类型 It.IsAnyType