使用moq验证调用带有参数的函数

16

我有一个ILogger接口,其中包含LogTrace(string value, params object[] parameters)方法。现在我想要验证是否调用了LogTrace方法,并且要记录的字符串中包含某个id。问题是它可能被不同地调用。例如: 1)LogTrace("MyString "+ id) 2)LogTrace("MyString {0}", id) 等等。

是否有一种好的方法可以使用Moq来验证所有情况?我只能想到创建一个手工模拟对象,将格式化的字符串用于验证。

3个回答

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

这个示例展示了如何验证参数列表不为空,并且只包含一种类型为int的参数5


我知道时间已经过去了,但是这样我就不知道它是否包含我想看的id。 - Ilya Chernomordik
这是我现在的做法,但如果记录方法不将id作为参数传递,而是像“Id =”+5这样做些事情,这种方法就无法帮助。我只是想让它更加健壮。 - Ilya Chernomordik
@IlyaChernomordik:我不理解。你能否编辑你的答案并包含一个完整的例子,说明你将要接受什么和拒绝什么? - Wiktor Zychla

3
我认为在这里做你需要的事情并没有简单的方法。问题在于,您需要确保将某些值的组合传递给您的方法,并且会产生许多不同的可验证场景:
  • 字符串包含ID并且参数包含ID-通过
  • 字符串包含ID但参数不包含ID-通过
  • 字符串不包含ID但参数包含ID = 通过
  • 字符串不包含ID且参数也不包含ID - 失败
然而,Moq不支持在可验证方法的不同参数之间进行此类条件表达式。一种可能的解决方案是检查ID的缺席,而不是其在任一参数中的存在。尝试类似以下内容的东西:
mock.Verify(m => m.LogTrace(
    It.Is<string>(s => !s.Contains(id)), 
    It.Is<object[]>(o => !o.Contains(id))), Times.Never());

我们所做的是验证是否满足“失败”条件 - 也就是说,您的字符串不包含id,您的对象数组也不包含。我们使用Times.Never()来确保这种情况永远不会发生。
但请记住,代码可能一开始并不明显;确保你在写代码时解释清楚你的意图。

这是一个有趣的建议,但很难验证日志是否被调用了两次。 - Ilya Chernomordik
这只是包含另一个Verify()的问题,以确保在此断言之前或之后调用该方法。无论您调用LogTrace多少次,此测试都应确保记录的字符串始终包含id。您确实应编写附加断言以确保至少调用一次该方法。 - rla4

1
你可以尝试使用“回调函数”,但它可能会变得非常复杂(未经测试):
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,但我正在考虑编写自己的存根来实现这个目的。 - Ilya Chernomordik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接