【问题标题】:Moq Verify Not Working as Expected After Upgrading to .NET Core 3.1升级到 .NET Core 3.1 后,Moq 验证未按预期工作
【发布时间】:2020-08-21 18:56:25
【问题描述】:

正如标题所暗示的,这在 .NET Core 2.2 和 Moq 版本 4.10.1 中适用于我们。升级到 .NET Core 3.1 和 Moq 版本 4.14.5 后,验证方法失败,说明未调用指定的方法(底层代码未更改)。我将 Moq 回滚到版本 4.10.1 只是为了看看它是否是由于新版本的 Moq 中的变化。我仍然遇到同样的错误。

尝试验证日志消息是否已写入 ILogger。

奇怪的是,如果我调试单元测试并使用变量 watch 查看模拟对象,它表明该方法确实已被调用。

相关代码:

public class AuditFilter, IResultFilter
{
    ...     
    public void OnResultExecuted( ResultExecutedContext context )
    {
        if( !IsContextValid( context ) )
        { return; }
        ...
    }
    
    public override bool IsContextValid( FilterContext context )
    {
        if( context == null )
        { 
            Logger.Error( "Error writing to the audit log", new ArgumentNullException( nameof( context ) ) ); 
            return false;
        }

        if( context.HttpContext == null )
        { 
            Logger.LogError( "Error writing to the audit log", new ArgumentNullException( nameof( context.HttpContext ) ) );
            return false;
        }

        return true;
    }       

    public ILogger Logger { get; set; }
    ...
}

public class AuditTests : BaseTests
{
    ...     
    private Mock<ILogger> _mockLog;
    private AuditFilter _filter;

    [SetUp]
    public void Setup()
    {
        _mockLog = new Mock<Microsoft.Extensions.Logging.ILogger>();
        _mockLog.SetupAllProperties;
        _filter = new AuditFilter();
    }

    [Test]
    public void Service_Filters_Audit_IsContextValid_Context_Null()
    {
        var expected = false;
    
        _filter.Logger = _mockLog.Object;
        var actual = _filter.IsContextValid( null );
    
        _mockLog.Verify( logger => logger.Log( LogLevel.Error, 
            It.IsAny<EventId>(), 
            It.IsAny<object>(),
            It.IsAny<ArgumentNullException>(), 
            It.IsAny<Func<object, Exception, string>>() ),
            Times.Once );
    
        Assert.AreEqual( expected, actual );
    }       
    ...
}

注意:方法 ILogger.LogError 是微软对 ILogger 的扩展方法,它调用 ILogger.Log 方法。

下面是抛出的异常。

注意:整数隐式转换为 Microsoft.Extensions.Logging.EventId,即 ILogger.Log 方法的第二个输入参数类型。

这些签名似乎与我相符;所以我不确定为什么它说它没有被调用。

重申一下,此代码在升级到 .NET Core 3.1 之前有效,并且在我们的预分叉代码中仍然有效。

另外,在有人建议需要设置该方法之前:在没有它的情况下升级之前它可以工作,我已经尝试过但它没有工作。

【问题讨论】:

  • 你已经看过这个了吗? github.com/moq/moq4/issues/918
  • 我看不到 _mockLog 的任何初始化
  • @SaiGummaluri,谢谢。该页面确实有我的解决方案。我在谷歌搜索时错过了它。如果您愿意,请将其作为答案提交,我会这样标记。我不得不更改验证方法调用中的最后一个参数 FROM: It.IsAny>() TO: (Func)It.IsAny()
  • @RoarS.,是的,我忽略了将它包含在上面列出的代码中。我会编辑帖子以包含它,但这不是问题。
  • @ELMOJO,很高兴它有所帮助:) 添加了围绕该问题的上下文及其推理,作为其他用户参考的答案。干杯!

标签: unit-testing asp.net-core .net-core moq


【解决方案1】:

我相信问题出在验证表达式的这一部分

It.IsAny<Func<object, Exception, string>>()

在幕后,它不是Func&lt;object, Exception, string&gt;,并且由于这种差异,模拟 IsAny 匹配器不匹配。如果修改为

(Func<object, Exception, string>) It.IsAny<object>()

它应该匹配。下面是我确定的验证表达式的起点,因为它至少适用于 .NET Core 2.* 及更高版本:

loggerMock.Verify(x => x.Log(
      It.IsAny<LogLevel>(),
      It.IsAny<EventId>(),
      It.IsAny<IReadOnlyList<KeyValuePair<string, object>>>(),
      It.IsAny<Exception>(),
      (Func<object, Exception, string>) It.IsAny<object>()),
   Times.Exactly(expectedNumberOfInvocations));

你可以使用It.IsAny&lt;object&gt;()作为第三个参数,但我发现这在查询属性时更方便。

我最近在工作中为黑盒进程做了大量的日志调用验证,所以现在我主要使用Moq.Contrib.ExpressionBuilders.Logging 来构建流畅、可读的验证表达式。免责声明:我是作者。

【讨论】:

    【解决方案2】:

    就像我在 cmets 中添加的一样,这是一个 known issue,具有最小起订量和 .NET Core,来自 .NET Core 3.0 的预览版。请查看 github 问题以了解更新为何会中断。

    简而言之,从链接的问题来看,这里的失败是由于传递给Logger.Log方法的类型现在更改为FormattedLogValues。尽管在 Moq 4.13.0 中引入了通用类型匹配器 It.IsAnyType,但这并不能解决问题。

    原因,作为Moq的作者之一states here

    重要的一点(目前)是It.IsAnyType 没有用于It.IsAny&lt;&gt; 类型参数中的嵌套位置。

    作为一种解决方法,我们应该替换 It.IsAny&lt;Func&lt;object, Exception, string&gt;&gt;()(Func&lt;object, Exception, string&gt;)It.IsAny&lt;object&gt;()

    为此提供的基本原理是提高类型匹配器的性能,因为前者会导致底层所有类型参数的分解,以查看是否首先包含类型匹配器。

    【讨论】:

    • 虽然分辨率正确,但 OP 没有使用It.IsAny&lt;Func&lt;It.IsAnyType, Exception, string&gt;&gt;(),所以这并不是问题的真正原因。原因是日志扩展传递了Func&lt;FormattedLogValues, Exception, string&gt; 参数,而It.IsAny&lt;Func&lt;object, Exception, string&gt;&gt;() 匹配器不起作用,仅仅是因为它是不同的类型。
    猜你喜欢
    • 2020-10-06
    • 2020-04-25
    • 2020-11-14
    • 2021-10-13
    • 1970-01-01
    • 2013-01-21
    • 1970-01-01
    • 2020-04-22
    • 2018-01-27
    相关资源
    最近更新 更多