Skip to content

Commit

Permalink
mock.Protected().Setup<int>("Foo") fails base implementation of Foo i…
Browse files Browse the repository at this point in the history
…s hidden in the derived class (#1341)
  • Loading branch information
VladimirKhvostov committed May 10, 2023
1 parent 1c4c723 commit 74d2528
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ The format is loosely based on [Keep a Changelog](http://keepachangelog.com/en/1

## Unreleased

#### Fixed

* mock.Protected().Setup<int>("Foo") fails base implementation of Foo is hidden in the derived class (#1341)

#### Added

* `Mock<T>.RaiseAsync` method for raising "async" events, i.e. events that use a `Func<..., Task>` or `Func<..., ValueTask>` delegate. (@stakx, #1313)
Expand Down
22 changes: 19 additions & 3 deletions src/Moq/Protected/ProtectedMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,24 @@ private static MethodInfo GetMethod(string methodName, Type[] genericTypeArgumen
.Select(m => m.MakeGenericMethod(genericTypeArguments));
}

return methods
.SingleOrDefault(m => m.GetParameterTypes().CompareTo(argTypes, exact, considerTypeMatchers: false));
methods = methods.Where(m => m.GetParameterTypes().CompareTo(argTypes, exact, considerTypeMatchers: false));

if (methods.Count() < 2)
{
return methods.FirstOrDefault();
}

for (Type type = typeof(T); type != typeof(object); type = type.BaseType)
{
var method = methods.SingleOrDefault(m => m.DeclaringType == typeof(T));

if (method != null)
{
return method;
}
}

return null;
}

private static Expression<Func<T, TResult>> GetMethodCall<TResult>(MethodInfo method, object[] args)
Expand Down Expand Up @@ -543,7 +559,7 @@ private static Expression ToExpressionArg(Type type, object arg)
}
return expression;
}

if (IsItRefAny(expression))
{
return expression;
Expand Down
66 changes: 65 additions & 1 deletion tests/Moq.Tests/ProtectedMockFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,39 @@ public void SetupResultAllowsProtectedMethodInBaseClass()
Assert.Equal(5, mock.Object.DoProtectedReturnGeneric<int>());
}

[Fact]
public void SetupResultAllowsHiddenVirtualMethods()
{
var mock = new Mock<FooDerived>();
mock.Protected()
.Setup<int>("ProtectedHiddenInt")
.Returns(5);

Assert.Equal(5, mock.Object.DoProtectedHiddenInt());
Assert.Equal(2, ((FooBase)mock.Object).DoProtectedHiddenInt());

mock.Protected()
.Setup<int>("ProtectedHiddenWithGenericParam", new[] { typeof(int) }, false)
.Returns(5);
Assert.Equal(5, mock.Object.DoProtectedHiddenWithGenericParam<int>());

mock.Protected()
.Setup<int>("ProtectedHiddenSum", genericTypeArguments: Array.Empty<Type>(), exactParameterMatch: true, 1, 2)
.Returns(3);

Assert.Equal(3, mock.Object.DoProtectedHiddenSum(1, 2));

mock.Protected()
.Setup<int>("ProtectedHiddenSum", genericTypeArguments: Array.Empty<Type>(), exactParameterMatch: true, 1, 2, 3)
.Returns(6);
Assert.Equal(6, mock.Object.DoProtectedHiddenSum(1, 2, 3));

mock.Protected()
.Setup<int>("ProtectedHiddenSum", genericTypeArguments: Array.Empty<Type>(), exactParameterMatch: true, 1, 2, 3, 4)
.Returns(10);
Assert.Equal(10, mock.Object.DoProtectedHiddenSum(1, 2, 3, 4));
}

[Fact]
public void SetupResultDefaulTwoOverloadsWithDerivedClassThrowsInvalidOperationException()
{
Expand Down Expand Up @@ -1041,7 +1074,7 @@ public void SetMatcherExpressionProperty(MethodCallExpression expression)
}

protected virtual LambdaExpression LambdaExpressionProperty { get; set; }

public void SetLambdaExpressionProperty(LambdaExpression expression)
{
LambdaExpressionProperty = expression;
Expand Down Expand Up @@ -1111,6 +1144,14 @@ public string DoTwoArgs(string arg, int arg1)
return this.TwoArgs(arg, arg1);
}

public int DoProtectedHiddenInt() => this.ProtectedHiddenInt();

public T DoProtectedHiddenWithGenericParam<T>() => this.ProtectedHiddenWithGenericParam<T>();

public int DoProtectedHiddenSum(int op1, int op2) => this.ProtectedHiddenSum(op1, op2);

public int DoProtectedHiddenSum(int op1, int op2, int op3) => this.ProtectedHiddenSum(op1, op2, op3);

public string GetProtectedValue()
{
return this.ProtectedValue;
Expand Down Expand Up @@ -1194,10 +1235,33 @@ protected virtual string TwoArgs(string arg, int arg1)
{
return arg;
}

protected virtual int ProtectedHiddenInt() => 2;

protected virtual T ProtectedHiddenWithGenericParam<T>() => default(T);

protected new virtual int ProtectedHiddenSum(int op1, int op2) => throw new NotImplementedException();

protected new virtual int ProtectedHiddenSum(int op1, int op2, int op3) => throw new NotImplementedException();
}

public class FooDerived : FooBase
{
protected new virtual int ProtectedHiddenInt() => 22;

protected new virtual T ProtectedHiddenWithGenericParam<T>() => default(T);

protected new virtual int ProtectedHiddenSum(int op1, int op2) => throw new NotImplementedException();

protected virtual int ProtectedHiddenSum(int op1, int op2, int op3, int op4) => throw new NotImplementedException();

public new int DoProtectedHiddenInt() => this.ProtectedHiddenInt();

public new T DoProtectedHiddenWithGenericParam<T>() => this.ProtectedHiddenWithGenericParam<T>();

public new int DoProtectedHiddenSum(int op1, int op2) => this.ProtectedHiddenSum(op1, op2);

public int DoProtectedHiddenSum(int op1, int op2, int op3, int op4) => this.ProtectedHiddenSum(op1, op2, op3, op4);
}

public class MyBase { }
Expand Down

0 comments on commit 74d2528

Please sign in to comment.