Mocking a protected generic method with Moq
It can be done since Moq 4.13 (2019-09-01) by using As
for protected methods and It.IsAnyType
for generic type arguments:
classUnderTest.Protected().As<IMyMethodMock>().Setup(x => x.MyMethod(It.IsAny<It.IsAnyType>())).Returns(...);
And create the following interface for the mocking method:
public interface IMyMethodMock
{
int MyMethod<T>(T data);
}
Full example:
[TestClass]
public class MockingProtectedGenericFixture
{
[TestMethod]
public void test()
{
// Arrange
var classUnderTest = new Mock<MyClass>();
classUnderTest.Protected().As<IMyMethodMock>().Setup(x => x.MyMethod(It.IsAny<It.IsAnyType>())).Returns(2);
// Act
var resForStringData = classUnderTest.Object.GetNumber("DataString");
var resForBooleanData = classUnderTest.Object.GetNumber(true);
// Assert
Assert.AreEqual(2, resForStringData);
Assert.AreEqual(2, resForBooleanData);
}
}
public class MyClass
{
public int GetNumber<T>(T data)
{
return MyMethod(data);
}
protected virtual int MyMethod<T>(T data)
{
return 1;
}
}
public interface IMyMethodMock
{
int MyMethod<T>(T data);
}
I've checked the source and it seems mocking protected generic methods with Moq is not supported:
The Protected()
method creates an instance of a class ProtectedMock<T>
which uses the following method to get the method that you want to mock:
private static MethodInfo GetMethod(string methodName, params object[] args)
{
return typeof(T).GetMethod(
methodName,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
null,
ToArgTypes(args),
null);
}
It uses Type.GetMethod to get the method for mocking, but GetMethod
(although MSDN states differently) don't play nice with generics, see:
GetMethod for generic method
Get a generic method without using GetMethods
Side note: In my opinion mocking a protected member is a code smell, and I would rather try to avoid it anyway with refactoring my design (beside that it's not supported in Moq).