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

Tags:

Moq