【问题标题】:Moq It.IsSubtype has no implicit reference conversionMoq It.IsSubtype 没有隐式引用转换
【发布时间】:2021-12-16 08:47:44
【问题描述】:

我正在尝试模拟一个具有T : IFoo 约束的泛型方法

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

类型 'Moq.It.IsSubtype' 不能在泛型类型或方法中用作类型参数“T” 'IMyClass.DoSomething(Action)'。没有隐式引用 从转换 'Moq.It.IsSubtype' 到 '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
        }
    }

【问题讨论】:

  • 从他们的文档看来是有效的:github.com/Moq/moq4/wiki/Quickstart#type-matchers-moq-413
  • @madreflection 对,但在这种情况下,嵌套泛型类型IsSubtype&lt;T&gt; 肯定不会实现IFoo
  • 你到底想在这里测试什么?显然传入的泛型将是IFoo 的子类型。如果不是它就不会编译?
  • @canton7:不,你是对的,它没有。它只实现了 Moq 自己的ITypeMatcher。我只是指出这不是语法错误(至少不是你想象的那样)。文档说这是你应该做的。在处理受约束的泛型类型参数时,这可能是库的限制。
  • @Liam 在我的真实代码 DoSomething 中返回一个 IDisposable,所以我希望它返回一个 Mock,但在最小示例中删除了它。否则我得到空异常

标签: c# moq


【解决方案1】:

由于It.IsSubtype&lt;IFoo&gt; 没有实现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());

【讨论】:

  • 感谢您的回答,不幸的是它不适用于我的完整示例。我用更多上下文扩展了原始帖子中的代码。因为 Action 没有通过 Action 设置
  • 好吧,如果你知道你要传入Action&lt;Foo&gt;,那么请改用Foomock.Setup(e =&gt; e.DoSomething&lt;Foo&gt;(It.IsAny&lt;Action&lt;Foo&gt;&gt;())).Returns(new Foo());
【解决方案2】:

您可以创建自己的类型匹配器,该匹配器将实现 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>());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-28
    • 2017-11-25
    • 1970-01-01
    • 2019-03-07
    • 1970-01-01
    • 1970-01-01
    • 2021-06-08
    相关资源
    最近更新 更多