Moq It.IsSubtype 没有隐式引用转换

Moq It.IsSubtype has no implicit reference conversion

我正在尝试模拟一个通用方法,它具有 T : IFoo

的约束

但是 moq 似乎无法理解转换并给出以下错误:

The type 'Moq.It.IsSubtype<SomeNamepace.IFoo>' cannot be used as type parameter 'T' in the generic type or method 'IMyClass.DoSomething(Action)'. There is no implicit reference conversion from 'Moq.It.IsSubtype<SomeNamepace.IFoo>' to 'SomeNamepace.IFoo'.

    public interface IFoo
    {
    }

    class Foo : IFoo
    {

    }

    public interface IMyClass
    {
        public IDisposable DoSomething<T>(Action<T> asd) where T : IFoo;
    }

    public class MyTest
    {
        [Test]
        public void SomeTest()
        {
            var mock = new Mock<IMyClass>();

            mock.Setup(e => e.DoSomething(It.IsAny<Action<IFoo>>())).Returns(Mock.Of<IDisposable>());

// What I want but gives compiler error
//             mock.Setup(e => e.DoSomething(It.IsAny<Action<It.IsSubtype<IFoo>>>())).Returns(Mock.Of<IDisposable>());


// Action<IFoo> would work, but in real code its not used like that
            Action<Foo> myAction = (e) => { };
            var result = mock.Object.DoSomething(myAction);

            result.Dispose(); // Null reference exception
        }
    }

因为It.IsSubtype<IFoo>没有实现IFoo,你不能这样使用它。由于类型限制。

但是,您可以单独使用 IFoo,这应该考虑您传入的任何值,因为它们都需要实现 IFoo。换句话说,类型约束已经为您完成了确保类型正确的繁重工作。例如,给定这样的设置:

public interface IFoo
{
}

public interface IMyClass
{
    string DoSomething<T>() where T : IFoo;
}

你的测试代码就是这样:

var mock = new Mock<IMyClass>();
mock.Setup(e => e.DoSomething<IFoo>()).Returns("cheese");

var result = mock.Object.DoSomething<IFoo>();

Assert.Equal("cheese", result); // true

编辑:

经过对问题的补充说明,上面的答案仍然成立,我们可以做类似的事情:

mock
    .Setup(e => e.DoSomething<Foo>(It.IsAny<Action<Foo>>()))
    .Returns(new MyDisposableObject());

现在我们正在使用 Foo,因为我们明确知道传入的类型,因此我们无需再担心接口。事实上,我们甚至可以简化代码,因为我们不再需要明确泛型类型:

mock
    .Setup(e => e.DoSomething(It.IsAny<Action<Foo>>()))
    .Returns(new MyDisposableObject());

您可以创建自己的类型匹配器,它将实现 IFoo 以匹配通用约束:

[TypeMatcher]
public class FooTypeMatcher<T> : IFoo, ITypeMatcher
    where T : IFoo
{
    bool ITypeMatcher.Matches(Type typeArgument)
    {
        return typeof(T).IsAssignableFrom(typeArgument);
    }
}

和设置:

mock.Setup(e => e.DoSomething(It.IsAny<Action<FooTypeMatcher<IFoo>>>())) // will match any Action accepting any IFoo implementation
    .Returns(Mock.Of<IDisposable>());