Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix ProtectedMock fails when arguments are of type Expression #1188 #1189

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1

#### Fixed

* mock.Protected() setup methods fail when argument is of type Expression (@tonyhallett, #1189)
* Parameter is invalid in Protected().SetupSet() ... VerifySet (@tonyhallett, #1186)

* Virtual properties and automocking not working for `mock.Protected().As<>()` (@tonyhallett, #1185)
Expand Down
72 changes: 53 additions & 19 deletions src/Moq/Protected/ProtectedMock.cs
Expand Up @@ -467,26 +467,15 @@ private static Type[] ToArgTypes(object[] args)
{
types[index] = ((MethodCallExpression)expr).Method.ReturnType;
}
else if (ItRefAnyField(expr) is FieldInfo itRefAnyField)
{
types[index] = itRefAnyField.FieldType.MakeByRefType();
}
else if (expr.NodeType == ExpressionType.MemberAccess)
{
var member = (MemberExpression)expr;
if (member.Member is FieldInfo field)
{
// Test for special case: `It.Ref<TValue>.IsAny`
if (field.Name == nameof(It.Ref<object>.IsAny))
{
var fieldDeclaringType = field.DeclaringType;
if (fieldDeclaringType.IsGenericType)
{
var fieldDeclaringTypeDefinition = fieldDeclaringType.GetGenericTypeDefinition();
if (fieldDeclaringTypeDefinition == typeof(It.Ref<>))
{
types[index] = field.FieldType.MakeByRefType();
continue;
}
}
}

types[index] = field.FieldType;
}
else if (member.Member is PropertyInfo property)
Expand All @@ -510,16 +499,61 @@ private static Type[] ToArgTypes(object[] args)
return types;
}

private static Expression ToExpressionArg(Type type, object arg)
private static bool IsItRefAny(Expression expression)
{
if (arg is LambdaExpression lambda)
return ItRefAnyField(expression) != null;
}

private static FieldInfo ItRefAnyField(Expression expr)
{
FieldInfo itRefAnyField = null;

if (expr.NodeType == ExpressionType.MemberAccess)
{
return lambda.Body;
var member = (MemberExpression)expr;
if (member.Member is FieldInfo field)
{
if (field.Name == nameof(It.Ref<object>.IsAny))
{
var fieldDeclaringType = field.DeclaringType;
if (fieldDeclaringType.IsGenericType)
{
var fieldDeclaringTypeDefinition = fieldDeclaringType.GetGenericTypeDefinition();
if (fieldDeclaringTypeDefinition == typeof(It.Ref<>))
{
itRefAnyField = field;
}
}
}
}
}

return itRefAnyField;
}

private static Expression ToExpressionArg(Type type, object arg)
{
if (arg is Expression expression)
{
return expression;
if (!type.IsAssignableFrom(expression.GetType()))
{
if(arg is LambdaExpression lambda)
{
expression = lambda.Body;
}
return expression;
}

if (IsItRefAny(expression))
{
return expression;
}

if (expression.IsMatch(out _))
{
return expression;
}

}

return Expression.Constant(arg, type);
Expand Down
95 changes: 95 additions & 0 deletions tests/Moq.Tests/ProtectedMockFixture.cs
Expand Up @@ -2,6 +2,7 @@
// All rights reserved. Licensed under the BSD 3-Clause License; see License.txt.

using System;
using System.Linq.Expressions;
stakx marked this conversation as resolved.
Show resolved Hide resolved

using Moq.Protected;

Expand Down Expand Up @@ -855,6 +856,70 @@ public void DoesNotThrowIfVerifySetPropertyTimesReached()
mock.Protected().VerifySet<string>("ProtectedValue", Times.Exactly(2), ItExpr.IsAny<string>());
}

[Fact]
public void SetupShouldWorkWithExpressionTypes()
{
var mock = new Mock<FooBase>();
var mocked = mock.Object;
var protectedMock = mock.Protected();

var expression = Expression.Constant(1);
Expression setExpression1 = null;
protectedMock.SetupSet<Expression>("ExpressionProperty", expression).Callback(expr => setExpression1 = expr);
mocked.SetExpressionProperty(expression);
Assert.Same(expression, setExpression1);

var expression2 = Expression.Constant(2);
Expression setExpression2 = null;
protectedMock.SetupSet<Expression>("ExpressionProperty", ItExpr.Is<ConstantExpression>(e => (int)e.Value == 2)).Callback(expr => setExpression2 = expr);
mocked.SetExpressionProperty(expression2);
Assert.Same(expression2, setExpression2);

var constantExpression = Expression.Constant(1);
ConstantExpression setConstantExpression = null;
protectedMock.SetupSet<ConstantExpression>("NotAMatcherExpressionProperty", constantExpression).Callback(expr => setConstantExpression = expr);
mocked.SetNotAMatcherExpressionProperty(constantExpression);
Assert.Same(constantExpression, setConstantExpression);

var constantExpression2 = Expression.Constant(2);
ConstantExpression setConstantExpression2 = null;
protectedMock.SetupSet<ConstantExpression>("NotAMatcherExpressionProperty", ItExpr.Is<ConstantExpression>(e => (int)e.Value == 2)).Callback(expr => setConstantExpression2 = expr);
mocked.SetNotAMatcherExpressionProperty(constantExpression2);
Assert.Same(constantExpression2, setConstantExpression2);

var method = typeof(FooBase).GetMethod(nameof(FooBase.MethodForReflection));
var methodCallExpression = Expression.Call(method);
MethodCallExpression setMethodCallExpression = null;
protectedMock.SetupSet<MethodCallExpression>("MatcherExpressionProperty", methodCallExpression).Callback(expr => setMethodCallExpression = expr);
mocked.SetMatcherExpressionProperty(methodCallExpression);
Assert.Same(methodCallExpression, setMethodCallExpression);

var methodCallExpression2 = Expression.Call(typeof(FooBase).GetMethod(nameof(FooBase.MethodForReflection2)));
MethodCallExpression setMethodCallExpression2 = null;
protectedMock.SetupSet<MethodCallExpression>("MatcherExpressionProperty", ItExpr.Is<MethodCallExpression>(e => e.Method != method)).Callback(expr => setMethodCallExpression2 = expr);
mocked.SetMatcherExpressionProperty(methodCallExpression2);
Assert.Same(methodCallExpression2, setMethodCallExpression2);

Expression<Func<int, bool>> lambdaExpression = i => i < 5;
LambdaExpression setLambdaExpression = null;
protectedMock.SetupSet<LambdaExpression>("LambdaExpressionProperty", lambdaExpression).Callback(expr => setLambdaExpression = expr);
mocked.SetLambdaExpressionProperty(lambdaExpression);
Assert.Same(lambdaExpression, setLambdaExpression);

Expression<Func<int, int>> lambdaExpression2 = i => i;
LambdaExpression setLambdaExpression2 = null;
protectedMock.SetupSet<LambdaExpression>("LambdaExpressionProperty", ItExpr.Is<LambdaExpression>(e => e == lambdaExpression2)).Callback(expr => setLambdaExpression2 = expr);
mocked.SetLambdaExpressionProperty(lambdaExpression2);
Assert.Same(lambdaExpression2, setLambdaExpression2);

}

public class ExpectedException : Exception
{
private static ExpectedException instance = new ExpectedException();
public static ExpectedException Instance => instance;
}

public class MethodOverloads
{
public void ExecuteDo(int a, int b)
Expand Down Expand Up @@ -952,6 +1017,36 @@ protected virtual FooBase Overloaded(MyDerived myBase)

public class FooBase
{
public static void MethodForReflection() { }
public static void MethodForReflection2() { }
protected virtual Expression ExpressionProperty { get; set; }

public void SetExpressionProperty(Expression expression)
{
ExpressionProperty = expression;
}

protected virtual ConstantExpression NotAMatcherExpressionProperty { get; set; }

public void SetNotAMatcherExpressionProperty(ConstantExpression expression)
{
NotAMatcherExpressionProperty = expression;
}

protected virtual MethodCallExpression MatcherExpressionProperty { get; set; }

public void SetMatcherExpressionProperty(MethodCallExpression expression)
{
MatcherExpressionProperty = expression;
}

protected virtual LambdaExpression LambdaExpressionProperty { get; set; }

public void SetLambdaExpressionProperty(LambdaExpression expression)
{
LambdaExpressionProperty = expression;
}

public virtual string PublicValue { get; set; }

protected internal virtual string ProtectedInternalValue { get; set; }
Expand Down