Martin Jespersen
Martin Jespersen

Reputation: 26183

Moq - Setup Generic Return type

How do you Setup a generic returntype in Moq?

I have the following interface:

public interface IFoo
{
   T Bar<T>();
}

Now I want to setup Bar<T>() to work inside a test

var foo = new Mock<IFoo>();
foo.Setup(x => x.Bar<It.IsAnyType>()).Returns(new Mock<???>().Object);

How do I fill out ???, or if that is not possible, then how do I return a generic mock?

There are 100s of types, I am trying to avoid mocking them all. The test breaks (because the code throws and exception) if the call returns null for any type, which is sadly the default non-mocked behavior.

Upvotes: 4

Views: 2350

Answers (3)

Benjamin H.
Benjamin H.

Reputation: 1

As was stated before, a reference type is required to create a mock:

public interface IFoo
{
    T Bar<T>() where T : class;
}

Now, it is possible to create a Mock<T> using reflection. This is really ugly, but it works also when interfaces are passed as T.

var fooMock = new Mock<IFoo>();
fooMock
    .Setup(foo => foo.Bar<It.IsAnyType>())
    .Returns(new InvocationFunc(call =>
    {
        // create type Mock<T> with the type passed to Bar<T>
        var typeArg = call.Method.GetGenericArguments()[0];
        var mockType = typeof(Mock<>).MakeGenericType(typeArg);
        var mock = Activator.CreateInstance(mockType);

        // find "Object" property, call get
        var objectProperty = mockType.GetProperty(nameof(Mock.Object), typeArg);
        return objectProperty.GetValue(mock);
    }));

Upvotes: 0

Peter
Peter

Reputation: 1121

Try using this Returns overload: IReturnsResult<TMock> Returns(InvocationFunc valueFunction);

Setup:

 factory.Setup(m => m.Create<It.IsAnyType>())
        .Returns(new InvocationFunc(invocation =>
                 {
                     var typeArgument = invocation.Method.GetGenericArguments()[0];
                     return Activator.CreateInstance(typeArgument);
                 });

Call:

 var something = factory.Object.Create<Something>();

Upvotes: 1

Nkosi
Nkosi

Reputation: 247443

If T is a reference type ie

public interface IFoo {
    T Bar<T>() where T : class;
}

Then it would be easier to just create a slim mock

public class MockFoo : IFoo {
    private MockFoo() {

    }

    public T Bar<T>() where T : class => Mock.Of<T>();

    public static IFoo New() => new MockFoo();
}

which can be initialized while arranging the test and using MOQ for mocking the output of Bar

//Arrange
IFoo foo =  MockFoo.New();

//...

Upvotes: 1

Related Questions