为什么是?? 操作员无法使用Moq设置方法

Chr*_*ert 2 c# null moq

有人可以向我解释,为什么这行不通?


builder.Setup(b => b.BuildCommand(query ?? It.IsAny<string>())).Returns(command);
Run Code Online (Sandbox Code Playgroud)

如果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);
Run Code Online (Sandbox Code Playgroud)

与代表有关吗?


编辑-完整示例

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);
}
Run Code Online (Sandbox Code Playgroud)

然后我可以这样称呼它

var command = new Mock<IQueryCommand>();
var builder = new Mock<IQueryCommandBuilder>();
builder.ReturnFromBuildCommand(command.Object);
Run Code Online (Sandbox Code Playgroud)

要么

string query = "SELECT Name FROM Persons;";
builder.ReturnFromBuildCommand(command.Object, query);
Run Code Online (Sandbox Code Playgroud)

取决于我是否关心该参数。

Joh*_*ght 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;
        });
    }
Run Code Online (Sandbox Code Playgroud)

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和/或对照已知类型进行检查来确定参数的Expression的类型,例如MatchExpression(类似Is.Any<string>()这样)。(参考:https : //github.com/moq/moq4/blob/master/Source/MatcherFactory.cs#L54

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

(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]))
Run Code Online (Sandbox Code Playgroud)

这就是第一个参数的样子。您可以重写代码以更好地表达Moq的外观,如下所示:

    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);

    }
Run Code Online (Sandbox Code Playgroud)

如果你把一个断点,你可以看到的价值nodeTypeCoalesce。现在,返回并将其更改为仅使用query,然后nodeType变为MemberAccess。使用It.IsAny<string>()nodeTypeCall

这就解释了这三种方法之间的差异以及为什么它的行为不如您预期的那样。null说实话,关于触发它的原因我尚不清楚,但是无论匹配器是什么,MatcherFactory.CreateMatcher似乎都认为这null是您的模拟配置的有效值。