gpt4 book ai didi

c# - 获取方法引用而不指定所有输入参数类型

转载 作者:太空宇宙 更新时间:2023-11-03 14:38:28 29 4
gpt4 key购买 nike

我想用 Moq 快速模拟一些方法调用。我确实有一些冗长的方法签名,它们的参数类型名称很长,而且我很懒,不想输入 It.IsAny<>()对于所有这些,当方法设置为无论如何都会失败时,不管输入参数的数量
更具体地说,我想出了这个扩展方法,但是 T1 (第一个参数)未绑定(bind)在示例调用中:

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 -我仍然可以引用Foo上的方法的方法以一种简单、快速的方式,但无需指定所有类型参数?

一些更多的救济/约束:

  • 您可以使用反射来收集有关该方法的信息(例如它的输入参数)。
  • 调用SetResult应该尽可能简单,最好只引用设置方法和返回值。
  • SetResult必须可扩展为任意数量的输入类型(即,上述示例调用中的 bar 也可能采用 T2T3、...)
    • 我知道这需要多个 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.* -调用只能在此表达式中进行(参见 this question and its comments 为什么我使用 *.invoke())。

endresult 不仅会是设置结果的方法,还会有异常,序列,...
但我可以自己解决这个问题。

最佳答案

这可能需要一点改进,但​​它似乎接近您所谈论的内容。唯一的区别是它目前需要 MethodInfo 而不是 Func 来获取它应该模拟的方法。

它遍历所有参数并使用 System.Linq.Expression 为每个参数创建一个 IsAny 表达式。

public static class MoqExtension
{
public static void SetResult<T, TResult>(this Mock<T> mock, MethodInfo methodInfo, TResult result)
where T : class
{
var expressions = new List<Expression>();
// Create IsAny for each parameter
foreach (var parameter in methodInfo.GetParameters())
{
var pType = parameter.ParameterType;

var isAnyMethod = typeof(It).GetMethods((BindingFlags)(-1))
.Where(x => x.Name == "IsAny")
.First();

var genericIsAnyMethod = isAnyMethod.MakeGenericMethod(pType);

var isAnyExp = Expression.Call(genericIsAnyMethod);
expressions.Add(isAnyExp);
}

// Create method call
var argParam = Expression.Parameter(typeof(T), "x");
var callExp = Expression.Call(argParam, methodInfo, expressions.ToArray());
var lambda = Expression.Lambda<Func<T, TResult>>(callExp, argParam);

// invoke setup method
var mockType = mock.GetType();
var setupMethod = mockType.GetMethods()
.Where(x => x.Name == "Setup" && x.IsGenericMethod)
.First();
var genericMethod = setupMethod.MakeGenericMethod(typeof(TResult));

var res = genericMethod.Invoke(mock, new object[] { lambda }) as ISetup<T, TResult>;

res.Returns(result);
}
}

使用示例:

    [Fact]
public void MoqTestDynamic2()
{
var m = new Mock<ITestInterface>();

m.SetResult(typeof(ITestInterface).GetMethod("GetAnotherInt"), 168);

Assert.Equal(168, m.Object.GetAnotherInt("s", 1, 3));
Assert.Equal(168, m.Object.GetAnotherInt("p", 1, 35));
Assert.Equal(168, m.Object.GetAnotherInt(null, 1, 3));
}

public interface ITestInterface
{
int GetInt(string s);

int GetAnotherInt(string s, int i, long l);
}

一定有更好的办法

  1. 表达我们想要模拟的方法,而不是传递一个 MethodInfo
  2. 传入我们可能想要的任何非默认过滤器

关于c# - 获取方法引用而不指定所有输入参数类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58992703/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com