我想要使用Moq快速模拟一些方法调用。我的某些方法签名很冗长,参数类型名称也非常长。我懒得为它们所有的输入参数都键入
更具体地说,我想到了这个扩展方法,但在示例调用中
现在我的IDE报错,因为在方法签名中没有明确使用类型T1,所以它不知道T1是什么。
如何更改SetResult方法,以便仍然可以简单快速地引用Foo上的该方法,但不需要指定所有类型参数?
一些其他解除/约束条件:
- 您可以使用反射来收集有关该方法的信息(例如其输入参数)。 - 对SetResult的调用应尽可能简单,理想情况下只引用要设置的方法和返回值。 - SetResult必须可扩展到任意数量的输入类型(也就是说,上面示例调用中的bar也可能接受T2、T3等)。
- 我知道这需要多个SetResult定义。
- 理想情况下,我希望对过滤器使用可选参数,这些参数从null回退到It.IsAny(如代码示例中所示)。我可以放弃这些过滤器并对所有参数使用It.IsAny。
特定于我的代码示例的一些问题(忽略一般问题):
Moq允许模拟函数调用并检查输入参数。这些是一些常规设置,我的上述SetResult方法应该归结为其中最后一个使用IsAny。
It.IsAny<>()
,因为这个方法不管输入参数有多少,都将失败。更具体地说,我想到了这个扩展方法,但在示例调用中
T1
(第一个参数)没有被绑定:public static void SetResult<T,T1,TResult>(this Mock<T> mock, Func<T, Func<T1, TResult>> method, TResult result, Func<T1> filterT1 = null)
where T : class
{
if(filterT1 == null)
{
filterT1 = It.IsAny<T1>;
}
mock.Setup(m => method.Invoke(m).Invoke(filterT1.Invoke()))
.Returns(result);
}
// I want to use this like the following
Mock<Foo> mock = /* ... */;
mock.SetResult(foo => foo.bar, "Some return value"); // Doesn't work
mock.SetResult<Foo, int, string>(foo => foo.bar, "Some return value"); // Works
mock.SetResult(foo => foo.bar, "Some return value", () => 42); // Works too
现在我的IDE报错,因为在方法签名中没有明确使用类型T1,所以它不知道T1是什么。
如何更改SetResult方法,以便仍然可以简单快速地引用Foo上的该方法,但不需要指定所有类型参数?
一些其他解除/约束条件:
- 您可以使用反射来收集有关该方法的信息(例如其输入参数)。 - 对SetResult的调用应尽可能简单,理想情况下只引用要设置的方法和返回值。 - SetResult必须可扩展到任意数量的输入类型(也就是说,上面示例调用中的bar也可能接受T2、T3等)。
- 我知道这需要多个SetResult定义。
- 理想情况下,我希望对过滤器使用可选参数,这些参数从null回退到It.IsAny(如代码示例中所示)。我可以放弃这些过滤器并对所有参数使用It.IsAny。
特定于我的代码示例的一些问题(忽略一般问题):
Moq允许模拟函数调用并检查输入参数。这些是一些常规设置,我的上述SetResult方法应该归结为其中最后一个使用IsAny。
// "Normal" setups
mock.Setup(m => m.bar(42)).Returns("Some value") // Only allow input 42
mock.Setup(m => m.bar(It.Is(i => i % 2 == 0))).Returns("Some value") // Only allow even inputs
mock.Setup(m => m.bar(It.IsAny<int>())).Returns("Some value") // Allow any int-input
Setup
需要一个 Expression<Func<TObject,TResult>>
(注意缺失的bar
输入类型的定义),并检查它以模拟调用。任何 It.*
调用只能在此表达式中进行(请参见这个问题及其注释,了解我为什么使用 *.invoke()
)。
最终结果不仅将是设置结果的方法,还包括异常、顺序等...但这些对我来说并不重要。