3

到目前为止,我们一直在按照这种方法ILogger.LogXXX来模拟调用。

不幸的是,在将项目更新到 .net core 3.0 之后,如果您使用严格的模拟 (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是内部的。

我总是可以更改模拟策略(从严格到宽松),但我更愿意保持原样(严格)。那么,关于如何解决这个问题的任何线索?

谢谢。

4

2 回答 2

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>())));

https://www.bountysource.com/issues/79804378-cannot-verify-calls-to-ilogger-in-net-core-3-0-preview-8-generic-type-matcher-doesn-t-work-带验证

于 2019-11-16T11:40:47.193 回答
0

这扩展了 Muk 的答案。

我的补充是“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);
        }


    }

}
于 2021-10-21T16:48:08.080 回答