Как издеваться над методами ILogger.LogXXX в netcore 3.0
До сих пор мы издевались над ILogger.LogXXX
призывает, следуя этому подходу.
К сожалению, после обновления проекта до.net core 3.0, и если вы используете строгие mocks (Moq), он всегда будет жаловаться на отсутствие соответствующей настройки:
Moq.MockException : ILogger.Log<FormattedLogValues>(LogLevel.Information, 0,
Inicio de cancelamento de reserva: Grm.GestaoFrotas.Dtos.Reservas.Mensagens.MsgCancelamentoReserva,
null,
Func<FormattedLogValues, Exception, string>) invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.
К сожалению, я не могу просто изменить объект с помощью FormattedLogValues
нравится:
_container.GetMock<ILogger<GestorReservas>>()
.Setup(l => l.Log(It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<FormattedLogValues>(),
It.IsAny<Exception>(),
It.IsAny<Func<FormattedLogValues, Exception, string>()));
Это не сработает, потому что FormattedLogValues
внутренний.
Я всегда могу изменить стратегию издевательства (строгий на свободный), но я бы предпочел оставить его таким, какой он есть (строгий). Итак, есть какие-нибудь подсказки о том, как решить эту проблему?
Спасибо.
3 ответа
Попробуй это:
// <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
// <PackageReference Include="Moq" Version="4.13.1" />
container.GetMock<ILogger<GestorReservas>>()
.Setup(l => l.Log(It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<It.IsAnyType>(),
It.IsAny<Exception>(),
(Func<It.IsAnyType, Exception, string>)It.IsAny<object>())));
Это расширяет ответ Мука.
Мое дополнение - "ILoggerFactory"... и то, как вы "кодируете" собственно логирование внутри вашего реального класса.
Во-первых, код модульного теста:
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
private Mock<ILoggerFactory> GetDefaultILoggerFactoryMock()
{
Mock<ILoggerFactory> returnMock = new Mock<ILoggerFactory>(MockBehavior.Loose);
returnMock.Setup(x => x.CreateLogger(It.IsAny<string>())).Returns(
() =>
this.GetDefaultILoggerMock<MyConcreteClassThatUsesILoggerFactoryInItsConstructor>().Object);
return returnMock;
}
private Mock<ILogger<T>> GetDefaultILoggerMock<T>()
{
Mock<ILogger<T>> returnMock = new Mock<ILogger<T>>(MockBehavior.Strict);
returnMock.Setup(
m => m.Log(
It.IsAny<LogLevel>(),
It.IsAny<EventId>(),
It.IsAny<object>(),
It.IsAny<Exception>(),
It.IsAny<Func<object, Exception, string>>())).Callback(
(LogLevel ll, EventId eid, object obj1, Exception ex, Func<object, Exception, string> func) =>
{
Console.WriteLine(func.Invoke(obj1, ex));
}).Verifiable();
returnMock.Setup(m => m.IsEnabled(It.IsAny<LogLevel>())).Returns(false);
return returnMock;
}
Теперь в вашем реальном классе (здесь я называю его «MyConcreteClassThatUsesILoggerFactoryInItsConstructor») .. вам нужно использовать МЕТОД НЕ РАСШИРЕНИЯ для выполнения вызовов журнала. (Все вспомогательные методы, такие как LogInformation, являются перегрузками статических методов расширения)
Ну вот так:
Func<object, Exception, string> logMsgFunc = (a, b) => "MyDebugMessageOne";
this.logger.Log(LogLevel.Debug, ushort.MaxValue, null, null, logMsgFunc);
или для того, что является исключением
catch (Exception ex)
{
Func<object, Exception, string> logMsgFunc = (a, b) => "MyErrorContextMessageTwo";
this.logger.Log(LogLevel.Error, ushort.MaxValue, null, ex, logMsgFunc);
}
Если вы выполните обе эти вещи, особенно вторую часть, вы можете издеваться над ILoggerFactory / ILogger
Вот необходимые части класса / конструктора:
using Microsoft.Extensions.Logging;
public class MyConcreteClassThatUsesILoggerFactoryInItsConstructor : IMyConcreteClassThatUsesILoggerFactoryInItsConstructor
{
public const string ErrorMsgILoggerFactoryIsNull = "ILoggerFactory is null";
private readonly ILogger<MyConcreteClassThatUsesILoggerFactoryInItsConstructor> logger;
public MyConcreteClassThatUsesILoggerFactoryInItsConstructor(
ILoggerFactory loggerFactory)
{
if (null == loggerFactory)
{
throw new ArgumentNullException(ErrorMsgILoggerFactoryIsNull, (Exception)null);
}
this.logger = loggerFactory.CreateLogger<MyConcreteClassThatUsesILoggerFactoryInItsConstructor>();
}
public void DoSomething()
{
Func<object, Exception, string> logMsgFunc = (a, b) => "DoSomething Started";
this.logger.Log(LogLevel.Debug, ushort.MaxValue, null, null, logMsgFunc);
int div = 0;
try
{
int x = 1 / div;
}
catch (Exception ex)
{
Func<object, Exception, string> errorMsgFunc = (a, b) => string.Format("MyErrorContextMessageTwo (div='{0}')", div);
this.logger.Log(LogLevel.Error, ushort.MaxValue, null, ex, errorMsgFunc);
}
}
}
https://adamstorr.azurewebsites.net/blog/mocking-ilogger-with-moq описывает это с помощью решения только Moq.
loggerMock
.Verify(l => l.Log(
It.Is<LogLevel>(l => l == LogLevel.Information),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => v.ToString() == "Message"),
It.IsAny<Exception>(),
It.IsAny<Func<It.IsAnyType, Exception, string>>()),
Times.AtLeastOnce);