开发者

Mocking generic method call for any given type parameter

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 开发者_JS百科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?


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


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


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.


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 )
{
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜