Насмешливый вызов универсального метода для любого заданного параметра типа - PullRequest
43 голосов
/ 15 марта 2011

У меня есть интерфейс

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

Я хотел бы посмеяться над тем, чтобы он просто возвращал новый экземпляр заданного типа, независимо от точного типа, что-то вроде:

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

(что, разумеется, не работает, потому что я не могу просто дать moq какой-либо параметр типа и не могу знать, какой тип должен быть возвращен.

Есть идеи на этот счет?

Ответы [ 3 ]

29 голосов
/ 15 марта 2011

Вместо использования макета, возможно, в вашем случае лучше использовать Заглушка .

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

Если вам действительно нужен макет (так что вы можете проверить, что GetDataDocumentназывался).Вместо того, чтобы пытаться бороться со структурой Mocking, иногда проще просто создать класс Mock прямо.

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

И чем в вашем тесте:

bool wasCalled = false;
IDataProvider dataProvider = new MockDataProvider(() => { wasCalled = true; });
var aTable = dataProvider.GetDataDocument<ATable>(new Guid());
Debug.Assert(wasCalled);
10 голосов
/ 15 марта 2011

Для конкретного теста, для которого вы собираетесь использовать этот макет, вы, вероятно, знаете, каким будет T, верно?

просто установите макет для этого:

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

На самом деле не рекомендуется повторно использовать макеты, так что продолжайте и настройте макеты для текущего теста под рукой.

6 голосов
/ 12 октября 2011

У меня была похожая проблема, я отказался от использования заглушки в этой ситуации, так как не хотел, чтобы дополнения к тестируемому интерфейсу требовали немедленных изменений в тестовом коде. то есть добавление нового метода не должно нарушать мои существующие тесты.

Чтобы заставить макет работать, я добавил все открытые типы в данной сборке во время выполнения.

//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 )
{
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...