【问题标题】:Using moq to verify a call to a function with param parameters使用 moq 验证对带有 param 参数的函数的调用
【发布时间】:2014-02-12 13:29:05
【问题描述】:

我有一个带有 LogTrace 的 ILogger 接口(字符串值,参数对象 [] 参数)。现在我想验证是否调用了 LogTrace 并且要记录的字符串包含一些 id。问题是它可以被不同地调用。例如。 1) LogTrace("MyString" + id) 2) LogTrace("MyString {0}", id) 等等。

起订量是否有一个很好的方法来验证所有场景?我只能想到创建一个手工制作的模拟,它将格式化可用于验证的字符串。

【问题讨论】:

    标签: c# function moq


    【解决方案1】:
    mock.Verify( m => m.LogTrace( It.IsAny<string>(), It.IsAny<object[]>() ) );
    

    params object[] 无论如何都会以object[] 的形式传递给方法,因此您只需要以某种方式匹配数组(例如,如上所述,它可以接受任何内容)。

    如果您需要对列表进行更多控制,请使用It.Is 匹配器,它允许您创建自己的谓词:

     mock.Verify( m => m.LogTrace( It.IsAny<string>(),
                It.Is<object[]>(ps =>
                    ps != null &&
                    ps.Length == 1 &&
                    ps[0] is int &&
                    (int)ps[0] == 5
                ) ) );
    

    这个例子展示了如何验证参数列表是否不为空并且包含5作为int类型的唯一参数。

    【讨论】:

    • 我知道它无论如何都通过了,但这样我不知道它是否包含我想在那里看到的 id。
    • 这就是我现在所做的,但如果日志记录方法确实将 id 作为参数传递,这将无济于事,而是执行“Id =” + 5 之类的操作。我只是想让它不那么脆弱
    • @IlyaChernomordik:我不明白。您能否编辑您的答案并包含一个完整的示例,说明您想要接受什么以及想要拒绝什么?
    【解决方案2】:

    我认为这里没有一种简单的方法可以满足您的需要。问题是您需要确保将某种值组合传递给您的方法,这会导致许多不同的可验证场景:

    • 字符串包含 ID AND 参数包含 ID - pass
    • 字符串包含 ID AND 参数不包含 ID - 通过
    • 字符串不包含 ID AND 参数包含 ID = pass
    • 字符串不包含 ID AND 参数不包含 ID - 失败

    但是,Moq 不支持可验证方法的不同参数之间的这种条件表达式。一种可能的解决方案是在任一参数中检查 id 的 absence,而不是其 presence。尝试类似:

    mock.Verify(m => m.LogTrace(
        It.Is<string>(s => !s.Contains(id)), 
        It.Is<object[]>(o => !o.Contains(id))), Times.Never());
    

    我们在这里所做的是验证是否满足 fail 条件 - 也就是说,您的字符串不包含 id,您的对象数组也不包含。我们使用 Times.Never() 来确保这种情况永远不会发生。

    但请记住,代码乍一看可能并不明显;确保在编写后正确解释您的意图。

    【讨论】:

    • 这是一个有趣的建议,但很难验证日志记录是否被调用了 2 次。
    • 这只是包含另一个 Verify() 以确保在此断言之前或之后调用该方法的问题。无论您调用多少次 LogTrace,此测试都应确保记录的字符串始终包含 id。您确实应该编写一个额外的断言来确保该方法至少被调用一次。
    【解决方案3】:

    您可以尝试使用Callback,但它变得相当复杂(未经测试):

    var mock = new Mock<ILogger>();
    string trace = null;
    mock.Setup(l => l.LogTrace(It.IsAny<string>(), It.IsAny<object[]>()))
            .Callback((s1, par) =>
                {
                    trace = string.Format(s1, par);
                });
    
    //rest of the test
    
    Assert.AreEqual(expected, trace);
    

    如果ILogger 有一个小接口,您可以考虑手动实现一个存根(这将保留它应该记录的所有行),并在测试结束时验证它。如果您记录了多行,那么这将是一个更具可读性的设置。

    【讨论】:

    • 它变得更加复杂,因为它并不总是被调用一次,我也需要验证。我刚刚测试了我的参数是否等于 id,但我正在考虑为此目的编写自己的存根
    猜你喜欢
    • 2012-11-09
    • 2011-03-08
    • 2011-07-11
    • 2010-10-18
    • 2011-06-24
    • 2022-06-10
    • 1970-01-01
    • 2013-04-06
    • 1970-01-01
    相关资源
    最近更新 更多