最后我得到了一个需要委托的解决方案。
我需要分析一个可以有多个参数的表达式,例如:
i => i.Car.Engine.Intake.Open()
我会将最后两个参数提取为“模块”和“方法”:
public class ExpressionTools
{
public string GetLastInstanceName<TProp, TDelegate>(Expression<Func<TProp, TDelegate>> expression)
{
var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var methodInfoExpression = (MemberExpression)methodCallExpression.Arguments.Reverse().Skip(1).First();
var instanceName = methodInfoExpression.Member.Name;
return instanceName;
}
public string GetMethodName<TProp, TDelegate>(Expression<Func<TProp, TDelegate>> expression)
{
var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
var methodInfo = (MemberInfo)methodInfoExpression.Value;
return methodInfo.Name;
}
}
代表解决方案
可以重用具有泛型的类,因为委托不需要分析表达式:
public class Verifier
{
public void Verify<TDelegate>(Expression<Func<KnownType, TDelegate>> methodExpression)
{
var tools = new ExpressionTools();
var moduleName = tools.GetLastInstanceName(methodExpression);
var methodName = tools.GetMethodName(methodExpression);
// My logic.
}
}
现在我需要创建代表,为此我创建了一个文件并在没有类的情况下声明它们:
namespace Whatever
{
public delegate void VoidDelegate();
public delegate void VoidBoolDelegate(bool value);
// etc...
}
最后,我需要维护一个类,该类具有用不同委托重载的相同方法。
public class FakeExtensions
{
public static void Verify(
this Fake fake,
Expression<Func<KnownType, VoidDelegate>> methodExpression)
{
var tools = new ExpressionTools();
var facilitator = new Verifier(fake, tools);
facilitator.Verify(methodExpression);
}
public static void Verify(
this Fake fake,
Expression<Func<KnownType, VoidBoolDelegate>> methodExpression)
{
var tools = new ExpressionTools();
var facilitator = new Verifier(fake, tools);
facilitator.Verify(methodExpression);
}
}
没有代表的替代解决方案
最简单的解决方案是为每个参数传递一个默认值,但这表示这些值也经过验证,但事实并非如此。我真的不喜欢有这样的想法:
this.helper.Verify(i => i.SomeModule.SomeMethod(0, false, 0.0, null);
由于 Moq 是我们测试框架的一部分,因此可以使用 It.IsAny 作为方法调用的每个参数的值,这表示值无关紧要:
this.helper.Verify(i => i.SomeModule.SomeMethod(It.IsAny<int>, It.IsAny<bool>, It.IsAny<double>, It.IsAny<SomeType>);
我为什么选择委托解决方案
我必须验证对具有多达 100 个参数的方法的低级第 3 方 API 上的一些调用。我认为维护委托列表和方法重载的负担在可维护性、可读性和易用性方面得到了回报。