使用Moq模拟受保护的泛型方法

Sun*_*nov 11 moq

要在Moq中模拟受保护的虚拟(非泛型)方法很简单:

public class MyClass
{
....
      protected virtual int MyMethod(Data data){..}
}
Run Code Online (Sandbox Code Playgroud)

并嘲笑它:

myMock.Protected().Setup<int>("MyMethod", ItExpr.Is<Data>( ...
Run Code Online (Sandbox Code Playgroud)

如果受保护的方法是通用的,我无法找到使用相同技术的方法,例如:

protected virtual int MyMethod<T>(T data)
Run Code Online (Sandbox Code Playgroud)

除了使用包装类来覆盖该方法之外,任何想法都是如此,我们非常感激.

nem*_*esv 6

我检查了源代码,似乎不支持使用Moq模拟受保护的泛型方法:

Protected()方法创建一个类的实例,该实例ProtectedMock<T>使用以下方法来获取要模拟的方法:

private static MethodInfo GetMethod(string methodName, params object[] args)
{
    return typeof(T).GetMethod(
           methodName,
           BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
           null,
           ToArgTypes(args),
           null);
}
Run Code Online (Sandbox Code Playgroud)

它使用Type.GetMethod来获取用于模拟的方法,但是GetMethod(尽管MSDN状态不同)对泛型不起作用,请参阅:

GetMethod用于泛型方法

在不使用GetMethods的情况下获取泛型方法

旁注:在我看来,模拟一个受保护的成员是一种代码味道,我宁愿尝试通过重构我的设计来避免它(除了它在Moq中不支持).

  • 我完全同意代码的味道。但有时在更复杂的场景中,这可以简化测试。即,如果您对一堆方法具有非常“相似”的功能,并且您真的只想验证公开公开的部分是否正确映射了参数以传递它们。 (2认同)

ita*_*aiy 5

自 Moq 4.13 (2019-09-01) 起,它可以通过As用于受保护的方法和It.IsAnyType泛型类型参数来完成:

classUnderTest.Protected().As<IMyMethodMock>().Setup(x => x.MyMethod(It.IsAny<It.IsAnyType>())).Returns(...);

并为模拟方法创建以下接口:

public interface IMyMethodMock
{
    int MyMethod<T>(T data);
}
Run Code Online (Sandbox Code Playgroud)

完整示例:

[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);
}
Run Code Online (Sandbox Code Playgroud)