Home > database >  Moq It.IsSubtype has no implicit reference conversion
Moq It.IsSubtype has no implicit reference conversion

Time:11-02

I am trying to mock a generic method, which has a constraint of T : IFoo

However moq cannot seem to understand the conversion and gives the following error:

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
        }
    }

CodePudding user response:

Since It.IsSubtype<IFoo> doesn't implement IFoo, you can't use it like this. due to the type constraint.

However, you can just use IFoo on its own, which should account for any value you pass in since they all need to implement IFoo. In other words, the heavy lifting of ensuring the type is correct is already done for you by the type constraint. For example, given a set-up like this:

public interface IFoo
{
}

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

Your test code would be simply this:

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

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

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

EDIT:

After the additional clarification to the question, the answer above still stands, and we can do something similar:

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

Now we are using Foo because we know explicitly the type being passed in so we don't need to worry about the interface anymore. In fact, we can even simplify the code since we don't need to be explicit about the generic type any more:

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

CodePudding user response:

You can create your own type matcher which will implement IFoo to match the generic constraint:

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

And setup:

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