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>());