【发布时间】:2020-03-18 10:26:33
【问题描述】:
我想用 Moq 快速模拟一些方法调用。我确实有一些冗长的方法签名,它们的参数类型名称很长,而且,虽然我很懒,但当方法设置失败时,不想为所有这些都输入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 是什么,因为在方法签名中没有明确使用该类型。
如何更改SetResult-Method,我仍然可以简单快速地引用Foo 上的方法,但无需指定所有类型参数?
更多缓解/约束:
- 您可以使用反射来收集有关该方法的信息(例如其输入参数)。
- 对
SetResult的调用应尽可能简单,最好只引用设置方法和返回值。 -
SetResult必须可扩展为任意数量的输入类型(即,上述示例中的bar调用也可能采用T2、T3、...)- 我知道这需要多个
SetResult-Definitions。
- 我知道这需要多个
- 理想情况下,我想为过滤器使用可选参数,这些参数从
null回退到It.IsAny(如代码示例中所示)。我可以在没有这些过滤器的情况下生活,并为所有参数使用It.IsAny。
一些特定于我的代码示例的绊线(忽略一般问题):
Moq 允许模拟函数调用并检查输入参数。这些是一些正常的设置,我上面的SetResult-Method 应该归结为(最后一个是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-input-types 缺少类型定义)并检查它以模拟调用。任何It.*-调用都只能在此表达式中进行(请参阅this question and its comments,了解我为什么使用*.invoke())。
最终结果不仅是设置结果的方法,还有异常、序列……
但我可以自己解决。
【问题讨论】: