为什么?运营商不使用Moq设置方法

时间:2017-07-06 07:18:15

标签: c# null moq

有人可以向我解释,为什么这不起作用?

builder.Setup(b => b.BuildCommand(query ?? It.IsAny<string>())).Returns(command);

如果querynullBuildCommand将通过null,而不是It.IsAny<string>()

相反,我必须这样做:

if(query == null)
    builder.Setup(b => b.BuildCommand(It.IsAny<string>())).Returns(command);
else
    builder.Setup(b => b.BuildCommand(query)).Returns(command);

它与代表有关吗?

编辑 - 完整示例

public static void ReturnFromBuildCommand(this Mock<IQueryCommandBuilder> builder, IQueryCommand command, string query = null)
{
    if(query == null)
        builder.Setup(b => b.BuildCommand(It.IsAny<string>())).Returns(command);
    else
        builder.Setup(b => b.BuildCommand(query)).Returns(command);
}

然后我可以称之为

var command = new Mock<IQueryCommand>();
var builder = new Mock<IQueryCommandBuilder>();
builder.ReturnFromBuildCommand(command.Object);

或者

string query = "SELECT Name FROM Persons;";
builder.ReturnFromBuildCommand(command.Object, query);

取决于我是否关心参数。

1 个答案:

答案 0 :(得分:5)

模拟的Setup方法接受一个表达式,然后Moq框架解构以确定被调用的方法及其参数。然后它设置一个拦截器以匹配参数。

您可以在Mock来源:

中看到这一点
    internal static MethodCallReturn<T, TResult> Setup<T, TResult>(
        Mock<T> mock,
        Expression<Func<T, TResult>> expression,
        Condition condition)
        where T : class
    {
        return PexProtector.Invoke(() =>
        {
            if (expression.IsProperty())
            {
                return SetupGet(mock, expression, condition);
            }

            var methodCall = expression.GetCallInfo(mock);
            var method = methodCall.Method;
            var args = methodCall.Arguments.ToArray();

            ThrowIfNotMember(expression, method);
            ThrowIfCantOverride(expression, method);
            var call = new MethodCallReturn<T, TResult>(mock, condition, expression, method, args);

            var targetInterceptor = GetInterceptor(methodCall.Object, mock);

            targetInterceptor.AddCall(call, SetupKind.Other);

            return call;
        });
    }

此处args的类型为Expression[]

(参考:https://github.com/moq/moq4/blob/master/Source/Mock.cs#L463

将此args数组作为参数MethodCallReturn传递给Moq类型arguments的构造函数。该构造函数(通过基类MethodCall)使用MatcherFactory.Create生成参数匹配器。 (参考:https://github.com/moq/moq4/blob/master/Source/MethodCall.cs#L148

这是事情开始变得有趣的地方! 在MatcherFactory.Create方法中,它尝试通过查看Expression.NodeType来确定参数的表达式类型和/或根据已知类型(例如MatchExpression进行检查)(这类似于{{ 1}}将是)。 (参考:https://github.com/moq/moq4/blob/master/Source/MatcherFactory.cs#L54

让我们退后一步。在你的特定情况下,代码Is.Any<string>()被编译成表达式本身 - 就像这个丑陋的混乱(由dotPeek反编译器生成):

query ?? Is.Any<string>()

这就是第一个论点的样子。您可以重写代码以更好地表达Moq看到的内容,例如:

(Expression) Expression.Coalesce((Expression) Expression.Field((Expression) Expression.Constant((object) cDisplayClass00, typeof (Extension.\u003C\u003Ec__DisplayClass0_0)), 
 FieldInfo.GetFieldFromHandle(__fieldref (Extension.\u003C\u003Ec__DisplayClass0_0.query))), 
(Expression) Expression.Call((Expression) null, (MethodInfo) MethodBase.GetMethodFromHandle(__methodref (It.IsAny)), new Expression[0]))

如果您设置断点,则可以看到 public static void ReturnFromBuildCommand(this Mock<IQueryCommandBuilder> builder, IQueryCommand command, string query = null) { Expression<Func<IQueryCommandBuilder, IQueryCommand>> expressOfFunc = commandBuilder => (commandBuilder.BuildCommand(query ?? It.IsAny<string>())); var methodCall = expressOfFunc.Body as MethodCallExpression; var args = methodCall.Arguments.ToArray(); var nodeType = args[0].NodeType; builder.Setup(expressOfFunc) .Returns(command); } 的值为nodeType。现在,返回并将其更改为仅使用Coalescequery变为nodeType。使用MemberAccessIt.IsAny<string>()nodeType

这解释了三种方法之间的差异以及为什么它不像你期望的那样。至于为什么它Call上的触发器对我来说并不清楚,说实话,但是null出来的任何匹配器似乎都认为MatcherFactory.CreateMatcher是模拟配置的有效值。 / p>