Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何测试asp.net内核内置Ilogger_C#_Unit Testing_Logging_Asp.net Core_Moq - Fatal编程技术网

C# 如何测试asp.net内核内置Ilogger

C# 如何测试asp.net内核内置Ilogger,c#,unit-testing,logging,asp.net-core,moq,C#,Unit Testing,Logging,Asp.net Core,Moq,我想验证一些日志记录。我正在使用asp.net core内置ILogger,并将其与asp.net core内置DI一起注入: private readonly ILogger<InvoiceApi> _logger; public InvoiceApi(ILogger<InvoiceApi> logger) { _logger = logger; } 并通过以下方式将其注入服务中进行测试: new InvoiceApi(MockLogger.Object);

我想验证一些日志记录。我正在使用asp.net core内置ILogger,并将其与asp.net core内置DI一起注入:

private readonly ILogger<InvoiceApi> _logger;

public InvoiceApi(ILogger<InvoiceApi> logger)
{
    _logger = logger;
}
并通过以下方式将其注入服务中进行测试:

new InvoiceApi(MockLogger.Object);
然后尝试验证:

MockLogger.Verify(m => m.LogError(It.Is<string>(s => s.Contains("CreateInvoiceFailed"))));
MockLogger.Verify(m=>m.LogError(It.Is(s=>s.Contains(“CreateInvoiceFailed”)));
但它抛出:

对非虚拟(在VB中可重写)成员的验证无效:m=>m.LogError


那么,如何验证记录的日志呢?

是一个扩展方法(静态)而不是实例方法。您不能用模拟框架“直接”模拟静态方法(因此是扩展方法),因此Moq无法模拟并验证该方法。我在网上看到过关于修改/包装目标界面并在其上进行模拟的建议,但如果您在许多地方的代码中都使用了默认的
ILogger
,这将意味着重写。您必须创建两个新类型,一个用于包装类,另一个用于可模拟接口。

正如@Nkosi已经说过的,您不能模拟扩展方法。你应该嘲笑的是,哪一个。这使得验证代码有点笨重,但它应该可以工作:

MockLogger.Verify(
    m => m.Log(
        LogLevel.Error,
        It.IsAny<EventId>(),
        It.Is<FormattedLogValues>(v => v.ToString().Contains("CreateInvoiceFailed")),
        It.IsAny<Exception>(),
        It.IsAny<Func<object, Exception, string>>()
    )
);
MockLogger.Verify(
m=>m.Log(
日志级别错误,
It.IsAny(),
It.Is(v=>v.ToString().Contains(“CreateInvoiceFailed”),
It.IsAny(),
It.IsAny()
)
);
(不确定这是否可以编译,但你可以理解要点)

我已经写了一篇文章,展示了各种方法,包括模仿下面的Log()方法,如其他答案所述。文章包括。最后,我建议您使用自己的适配器,而不是直接使用ILogger类型,如果您需要能够测试它是否被调用的话


升级到.net core 3.1后,FormattedLogValues将变为内部。我们不能再访问它了。我对extensions方法做了一些修改。 扩展方法的一些示例用法:

mockLogger.VerifyLog(Times.Once);
publicstaticvoidverifylog(此模拟模拟模拟记录器,Func时报)
{
mockLogger.Verify(x=>x.Log(
It.IsAny(),
It.IsAny(),
它是((v,t)=>真的),
It.IsAny(),
它是((v,t)=>真的)),次);
}

对于尝试接收日志调用回调的用户:

            mock.Mock<ILogger<X>>()
                .Setup(x =>
                    x.Log(
                        LogLevel.Information,
                        It.IsAny<EventId>(),
                        It.IsAny<It.IsAnyType>(),
                        It.IsAny<Exception>(),
                        (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()
                    )
                )
                .Callback<LogLevel, EventId, object, Exception, Delegate>(
                    (level, eventid, state, ex, func) =>
                    {
                        this.Out.WriteLine(state.ToString());
                    }
                );
mock.mock()
.Setup(x=>
x、 日志(
日志级别。信息,
It.IsAny(),
It.IsAny(),
It.IsAny(),
(Func)It.IsAny()
)
)
.回拨(
(级别、事件ID、状态、ex、func)=>
{
this.Out.WriteLine(state.ToString());
}
);

我必须对工作中的黑盒流程进行大量的日志验证,在对表达式进行了一段短时间的忍受之后,我放弃了,并组装了一个表达式生成器-

最终结果是一个简短、流畅的验证声明:

logger.Verify(
  Log.With.LogLevel(LogLevel.Error)
    .And.EventId(666)
    .And.LogMessage("I am a meat popsicle"), 
  Times.Once);
下面是一个示例,说明如何验证记录器的使用情况

假设我们有一个要模拟的
ILogger

private readonly Mock loggerMock=new Mock();
验证零呼叫
loggerlock.Verify(l=>l.Log(It.IsAny(),It.IsAny(),
It.IsAny(),It.IsAny(),
(Func)It.IsAny()),
时代,永远不会);
验证日志级别和时间
const LogLevel expectedLevel=LogLevel.Debug;
预期发生次数=次数,精确为(1);
验证(l=>l.Log(expectedLevel,It.IsAny()),
It.IsAny(),It.IsAny(),
(Func)It.IsAny()),
预期事件);
验证日志消息
const LogLevel expectedLevel=LogLevel.Information;
常量字符串expectedMessage=“信息”;
const Exception withoutException=null;
预期发生次数=次。一次;
验证(logger=>logger.Log(expectedLevel,It.IsAny()),
It.IsAny(),毫无例外,
(Func)It.IsAny()),
预期事件、预期消息);

const LogLevel expectedLevel=LogLevel.Information;
常量字符串expectedMessage=“信息”;
const Exception withoutException=null;
预期发生次数=次。一次;
验证(logger=>logger.Log(expectedLevel,It.IsAny()),
It.Is((v,)=>v.ToString().Contains(expectedMessage)),
毫无例外,(Func)It.IsAny()),
预期事件);
验证异常类型
const LogLevel expectedLevel=LogLevel.Critical;
预期发生次数=次。一次;
验证(l=>l.Log(expectedLevel,It.IsAny()),
It.IsAny(),It.IsAny(),
(Func)It.IsAny()),
预期事件);

是一个扩展方法(静态)而不是实例方法。Moq无法模拟并因此验证method.IMO将LogError、LogDebug等方法作为扩展方法是微软非常独特的设计。这些方法看起来像ILogger接口的一级公民。如果所有的东西都是扩展,那么这个界面还剩下什么?它确实是!唯一的问题是添加
It.IsAny()
作为倒数第二个参数。感谢@khellang使用.Log而不是.LogError!这在.NETCore3.0中中断,因为FormattedLogValues变为内部。这太糟糕了。看起来新版本的Moq(v4.13)允许您使用
It.IsAnyType
而不是
FormattedLogValues
,因此您仍然可以在3.0中模拟它。请参阅。
It.IsAnyType
work!这是来自上面提到的链接的有效解决方案:
\u loggerlock.Verify(x=>x.Log(LogLevel.Warning,It.IsAny(),It.IsAny(),It.IsAny(),(Func)It.IsAny())自FormattedLogValues内部化以来,有关于.Callback设置的任何帮助吗?这里也可以看到。提前感谢您提供的任何提示。我只需要验证记录的消息内容,这可以根据需要工作。
logger.Verify(
  Log.With.LogLevel(LogLevel.Error)
    .And.EventId(666)
    .And.LogMessage("I am a meat popsicle"), 
  Times.Once);
const LogLevel expectedLevel = LogLevel.Debug;
Times expectedOccasions = Times.Exactly(1);

loggerMock.Verify(l => l.Log(expectedLevel, It.IsAny<EventId>(), 
  It.IsAny<It.IsAnyType>(), It.IsAny<Exception>(), 
  (Func<It.IsAnyType, Exception, string>)It.IsAny<object>()),  
  expectedOccasions);