Mocking generic method call for any given type parameter

后端 未结 4 1796
孤独总比滥情好
孤独总比滥情好 2020-12-05 12:35

I have an interface

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

I\'d like to mock

相关标签:
4条回答
  • 2020-12-05 13:22

    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 讨论(0)
  • 2020-12-05 13:23

    With Moq 4.13 or later you can do this

    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);
    }));
    
    0 讨论(0)
  • 2020-12-05 13:26

    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.

    0 讨论(0)
  • 2020-12-05 13:34

    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);
    
    0 讨论(0)
提交回复
热议问题