有人可以向我解释,为什么这不起作用?
builder.Setup(b => b.BuildCommand(query ?? It.IsAny<string>())).Returns(command);
如果query
为null
,BuildCommand
将通过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);
取决于我是否关心参数。
答案 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
。现在,返回并将其更改为仅使用Coalesce
,query
变为nodeType
。使用MemberAccess
,It.IsAny<string>()
为nodeType
。
这解释了三种方法之间的差异以及为什么它不像你期望的那样。至于为什么它Call
上的触发器对我来说并不清楚,说实话,但是null
出来的任何匹配器似乎都认为MatcherFactory.CreateMatcher
是模拟配置的有效值。 / p>