如何使用 It.IsAny 作为参数?

Joh*_*Boy 5 c# moq

我有一个调用模拟的测试类MyClass,我以前和以前Setup都这样做过。DoStuffADoStuffB

我尝试Verify在一个方法中包装多个调用,如下所示:

void VerifyMany(int input)
{
    _myClassMock.Verify(ic => ic.DoStuffA(input), Times.Once());
    _myClassMock.Verify(ic => ic.DoStuffB(input), Times.Once());
}
Run Code Online (Sandbox Code Playgroud)

如果我将我的方法It.IsAny<int>()作为输入调用VerifyMany(It.IsAny<int>())- 我的测试没有通过,但是如果我直接使用 It.IsAny 调用验证方法,它将起作用:

_myClassMock.Verify(ic => ic.DoStuffA(It.IsAny<int>()), Times.Once());
_myClassMock.Verify(ic => ic.DoStuffB(It.IsAny<int>()), Times.Once());
Run Code Online (Sandbox Code Playgroud)

我从这个问题的答案中了解到,当指定给 Setup/Verify 时,Moq 在表达式中处理 It.IsAny 的方式不同,是否有任何解决方法?

Vin*_*ent 5

这不起作用的原因:

void VerifyMany(int input)
{
    _myClassMock.Verify(ic => ic.DoStuffA(input), Times.Once());
    _myClassMock.Verify(ic => ic.DoStuffB(input), Times.Once());
}
Run Code Online (Sandbox Code Playgroud)

是因为It.IsAny<int>()(当简单调用时)返回 0。所以本质上,当VerifyMany使用 调用时It.IsAny<int>(),您将 0 传递给VerifyMany。这又意味着DoStuffA(0)DoStuffB(0)被验证(不是您可能想要的任何整数值)。

另一个调用:

_myClassMock.Verify(ic => ic.DoStuffA(It.IsAny<int>()), Times.Once());
_myClassMock.Verify(ic => ic.DoStuffB(It.IsAny<int>()), Times.Once());
Run Code Online (Sandbox Code Playgroud)

确实有效,因为该ic => ic.DoStuffA(It.IsAny<int>()部分从未被直接调用。它被转换成一个表达式树,而 Moq 只遍历(或访问,如果您愿意的话)该表达式树。这意味着它将在表达式树中找到It.IsAny<int>(),然后能够验证DoStuffA对任何整数值的调用(它也在同一表达式树中找到)。(有关表达式树的更多信息,请随时阅读此内容

您可以通过创建一个方法来完成这个半工作,该方法抽象对away的调用Verify并接受如下表达式:

void VerifyOnce(Expression<Action<ClassMockIsBasedOn>> callToVerify)
{
    _myClassMock.Verify(callToVerify, Times.Once());
}
Run Code Online (Sandbox Code Playgroud)

这允许您像这样调用它:

VerifyOnce(ic => ic.DoStuffA(It.IsAny<int>())
Run Code Online (Sandbox Code Playgroud)

您还可以扩展该VerifyOnce示例以接受多个表达式。这将允许您在一行上验证DoStuffA和:DoStuffB

void VerifyOnce(params Expression<Action<ClassMockIsBasedOn>>[] callsToVerify)
{
    foreach(var callToVerify in callsToVerify) 
    {
        _myClassMock.Verify(callToVerify, Times.Once());
    }
}
Run Code Online (Sandbox Code Playgroud)

这将允许像这样的调用:

VerifyOnce(ic => ic.DoStuffA(It.IsAny<int>(),
           ic => ic.DoStuffB(It.IsAny<int>());
Run Code Online (Sandbox Code Playgroud)

当然你也可以ClassMockIsBasedOn用通用的来代替。并添加一个重载,允许方法返回值(而不是 void)或接受多个参数,如 Brett 在评论中建议的那样。

  • @JohnoBoy我认为Vincent提供了一个传递表达式并在`.Verify`调用中使用它的示例,但是您可以只传递一个表达式数组并使用foreach循环它们..`IEnumerable&lt;Action&lt;ClassMockIsBasedOn&gt;&gt;调用验证` ... (2认同)