57

I have an interface

public interface IDataProvider
{
    T GetDataDocument<T>(Guid document) where T:class, new()
}

I'd like to mock it in a way, that it would just return a new instance of a given type, regardless of the exact type, something like:

myMock.Setup(m => m.GetDataDocument<It.IsAny<Type>()>(It.IsAny<Guid>()))
.Returns(() => new T());

(which doesn't work of course, because I cannot just give any type parameter to moq, and I can't know which type must be returned.

Any ideas on this one?

Hassan
  • 2,603
  • 2
  • 19
  • 18

4 Answers4

33

Instead of using a mock, maybe your case would be better to use a Stub.

public class StubDataProvider : IDataProvider
{
    public T GetDataDocument<T>(Guid document) where T : class, new()
    {
        return new T();
    }
}

If you truly need a mock (so you can verify that GetDataDocument was called). Instead of trying to wrestle with a Mocking framework it sometimes is easier to just create a Mock class out right.

public class MockDataProvider : IDataProvider
{
    private readonly Action _action;

    public MockDataProvider(Action action)
    {
        _action = action;
    }

    public T GetDataDocument<T>(Guid document) where T : class, new()
    {
        _action();
        return new T();
    }
}

And than in your test:

bool wasCalled = false;
IDataProvider dataProvider = new MockDataProvider(() => { wasCalled = true; });
var aTable = dataProvider.GetDataDocument<ATable>(new Guid());
Debug.Assert(wasCalled);
Community
  • 1
  • 1
Mark Coleman
  • 40,542
  • 9
  • 81
  • 101
  • 3
    Good solution, although I'd like to see some mock/stub framework do this automagically :) I'll try to wait a bit, maybe another answer will tip up with an auotomatic solution. – Hassan Mar 15 '11 at 12:29
20

With Moq 4.13 or later you can use

  • It.IsAnyType — matches any type
  • It.IsSubtype<T> — matches T and proper subtypes of T
  • It.IsValueType — matches only value types

To get the value of the generic argument or do some other operation with the original method, you can use IInvocation parameter of InvocationAction or InvocationFunc

  • setup.Callback(new InvocationAction(invocation => ...))
  • setup.Returns(new InvocationFunc(invocation => ...))

Here is an example:

var myMock = new Mock<IDataProvider>();
myMock.Setup(m => m.GetDataDocument<It.IsAnyType>(It.IsAny<Guid>()))
    .Returns(new InvocationFunc(invocation =>
    {
        var type = invocation.Method.GetGenericArguments()[0];
        return Activator.CreateInstance(type);
    }));
Vijay Nirmal
  • 5,239
  • 4
  • 26
  • 59
12

For the particular test you are going to use this mock for, you probably know what T will be, right?

simply just setup the mock for that:

myMock.Setup(m => m.GetDataDocument<MyDataClass>()>(It.IsAny<Guid>()))
   .Returns(() => new MyDataClass());

It's not really recommended to reuse the mocks anyway, so go ahead and setup mocks for the actual test at hand.

Mikael Östberg
  • 16,982
  • 6
  • 61
  • 79
  • 44
    no, the whole point is, that I don't know the exact type, I know that several types will be used, but I'm too lazy to write different setup clauses for them all :) – Hassan Mar 15 '11 at 12:26
  • 1
    There is also a case where the method being mocked is using a type definition that is internal to the assembly being mocked and can't be used when defining a setup. – Josh Gust May 29 '20 at 17:58
7

I had a similar issue, I chose against using a stub in this situation as I did not want additions to the interface being tested to require immediate changes to the test code. i.e. adding a new method should not break my existing tests.

To get the mock working I added all the public type in a given assembly at runtime.

//This is fairly expensive so cache the types
static DummyRepository()
{
    foreach( var type in typeof( SomeTypeInAssemblyWithModelObjects ).Assembly.GetTypes() )
    {
        if( !type.IsClass | type.IsAbstract || !type.IsPublic || type.IsGenericTypeDefinition )
        {
            continue;
        }

        g_types.Add( type );
    }
}

public DummyRepository()
{
    MockRepository = new Mock<ISomeRepository>();

    var setupLoadBy = GetType().GetMethod( "SetupLoadBy", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod );

    foreach( var type in g_types )
    {
        var loadMethod = setupLoadBy.MakeGenericMethod( type );
        loadMethod.Invoke( this, null );
    }
}

private void SetupLoadBy<T>()
{
    MockRepository.Setup( u => u.Load<T>( It.IsAny<long>() ) ).Returns<long>( LoadById<T> );
}

public T LoadById<T>( long id )
{
}
Andre
  • 4,560
  • 2
  • 21
  • 27