如何使用 It.IsAny 作为参数?
How can I use It.IsAny as a parameter?
我有一个测试 class 调用 MyClass
的模拟,我之前 Setup
DoStuffA
和 DoStuffB
。
我试过在一个方法中包含多个 Verify
调用,如下所示:
void VerifyMany(int input)
{
_myClassMock.Verify(ic => ic.DoStuffA(input), Times.Once());
_myClassMock.Verify(ic => ic.DoStuffB(input), Times.Once());
}
如果我使用 It.IsAny<int>()
作为输入调用我的方法 - VerifyMany(It.IsAny<int>())
- 我的测试没有通过,但是如果我直接使用 It.IsAny 调用 Verify 方法它会起作用:
_myClassMock.Verify(ic => ic.DoStuffA(It.IsAny<int>()), Times.Once());
_myClassMock.Verify(ic => ic.DoStuffB(It.IsAny<int>()), Times.Once());
我从 this 问题的答案中了解到,当指定给 Setup/Verify 时,Moq 在表达式中处理 It.IsAny 的方式不同,是否有任何解决方法?
如果在 Setup
结构中使用,It.IsAny
只允许 Moq 匹配未来的方法调用调用,如果使用 Verify
或 Assert
则不能您的 Setup
尚未设置 It.IsAny
。因为It.IsAny
总是return输入的默认值。
所以如果你想在 Verify
处使用 It.IsAny
你应该先 Setup
作为目标方法:
正确
_myClassMock.Setup(ic => ic.DoStuffA(It.IsAny<int>())).Returns(// Something here);
_myClassMock.Verify(ic => ic.DoStuffA(It.IsAny<int>()), Times.Once());
错误
_myClassMock.Setup(ic => ic.DoStuffA(1)).Returns(// Something here);
_myClassMock.Verify(ic => ic.DoStuffA(It.IsAny<int>()), Times.Once());
这不起作用的原因:
void VerifyMany(int input)
{
_myClassMock.Verify(ic => ic.DoStuffA(input), Times.Once());
_myClassMock.Verify(ic => ic.DoStuffB(input), Times.Once());
}
是因为 It.IsAny<int>()
(当简单调用时)returns 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());
之所以有效,是因为从不直接调用 ic => ic.DoStuffA(It.IsAny<int>()
部分。它变成了一个表达式树,Moq 只遍历(或者如果你愿意的话,访问)那个表达式树。这意味着它将在表达式树中找到 It.IsAny<int>()
,然后能够使用任何整数值验证对 DoStuffA
的调用(它也在同一表达式树中找到)。 (有关表达式树的更多信息,请随时 read this)
您可以通过创建一个方法来完成这个半工作,该方法将对 Verify
的调用抽象化并接受如下表达式:
void VerifyOnce(Expression<Action<ClassMockIsBasedOn>> callToVerify)
{
_myClassMock.Verify(callToVerify, Times.Once());
}
这允许你这样调用:
VerifyOnce(ic => ic.DoStuffA(It.IsAny<int>())
您还可以扩展 VerifyOnce
示例以接受多个表达式。这将允许您在一行上验证 DoStuffA
和 DoStuffB
:
void VerifyOnce(params Expression<Action<ClassMockIsBasedOn>>[] callsToVerify)
{
foreach(var callToVerify in callsToVerify)
{
_myClassMock.Verify(callToVerify, Times.Once());
}
}
这将允许这样的调用:
VerifyOnce(ic => ic.DoStuffA(It.IsAny<int>(),
ic => ic.DoStuffB(It.IsAny<int>());
当然,您可以将 ClassMockIsBasedOn
替换为通用的。并添加一个重载,允许 return 值(而不是无效)或接受多个参数的方法,如 Brett 在评论中所建议的那样。
我有一个测试 class 调用 MyClass
的模拟,我之前 Setup
DoStuffA
和 DoStuffB
。
我试过在一个方法中包含多个 Verify
调用,如下所示:
void VerifyMany(int input)
{
_myClassMock.Verify(ic => ic.DoStuffA(input), Times.Once());
_myClassMock.Verify(ic => ic.DoStuffB(input), Times.Once());
}
如果我使用 It.IsAny<int>()
作为输入调用我的方法 - VerifyMany(It.IsAny<int>())
- 我的测试没有通过,但是如果我直接使用 It.IsAny 调用 Verify 方法它会起作用:
_myClassMock.Verify(ic => ic.DoStuffA(It.IsAny<int>()), Times.Once());
_myClassMock.Verify(ic => ic.DoStuffB(It.IsAny<int>()), Times.Once());
我从 this 问题的答案中了解到,当指定给 Setup/Verify 时,Moq 在表达式中处理 It.IsAny 的方式不同,是否有任何解决方法?
Setup
结构中使用,It.IsAny
只允许 Moq 匹配未来的方法调用调用,如果使用 Verify
或 Assert
则不能您的 Setup
尚未设置 It.IsAny
。因为It.IsAny
总是return输入的默认值。
所以如果你想在 Verify
处使用 It.IsAny
你应该先 Setup
作为目标方法:
正确
_myClassMock.Setup(ic => ic.DoStuffA(It.IsAny<int>())).Returns(// Something here);
_myClassMock.Verify(ic => ic.DoStuffA(It.IsAny<int>()), Times.Once());
错误
_myClassMock.Setup(ic => ic.DoStuffA(1)).Returns(// Something here);
_myClassMock.Verify(ic => ic.DoStuffA(It.IsAny<int>()), Times.Once());
这不起作用的原因:
void VerifyMany(int input)
{
_myClassMock.Verify(ic => ic.DoStuffA(input), Times.Once());
_myClassMock.Verify(ic => ic.DoStuffB(input), Times.Once());
}
是因为 It.IsAny<int>()
(当简单调用时)returns 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());
之所以有效,是因为从不直接调用 ic => ic.DoStuffA(It.IsAny<int>()
部分。它变成了一个表达式树,Moq 只遍历(或者如果你愿意的话,访问)那个表达式树。这意味着它将在表达式树中找到 It.IsAny<int>()
,然后能够使用任何整数值验证对 DoStuffA
的调用(它也在同一表达式树中找到)。 (有关表达式树的更多信息,请随时 read this)
您可以通过创建一个方法来完成这个半工作,该方法将对 Verify
的调用抽象化并接受如下表达式:
void VerifyOnce(Expression<Action<ClassMockIsBasedOn>> callToVerify)
{
_myClassMock.Verify(callToVerify, Times.Once());
}
这允许你这样调用:
VerifyOnce(ic => ic.DoStuffA(It.IsAny<int>())
您还可以扩展 VerifyOnce
示例以接受多个表达式。这将允许您在一行上验证 DoStuffA
和 DoStuffB
:
void VerifyOnce(params Expression<Action<ClassMockIsBasedOn>>[] callsToVerify)
{
foreach(var callToVerify in callsToVerify)
{
_myClassMock.Verify(callToVerify, Times.Once());
}
}
这将允许这样的调用:
VerifyOnce(ic => ic.DoStuffA(It.IsAny<int>(),
ic => ic.DoStuffB(It.IsAny<int>());
当然,您可以将 ClassMockIsBasedOn
替换为通用的。并添加一个重载,允许 return 值(而不是无效)或接受多个参数的方法,如 Brett 在评论中所建议的那样。