Вызывающий общий вызов метода для любого заданного параметра типа
У меня есть интерфейс
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, и я не могу знать, какой тип должен быть возвращен.
Любые идеи по этому поводу?
Ответы
Ответ 1
Вместо использования макета, возможно, ваш случай будет лучше использовать Stub.
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);
Ответ 2
Для конкретного теста, на который вы собираетесь использовать этот макет, вы, вероятно, знаете, что такое T, правильно?
просто настройте макет для этого:
myMock.Setup(m => m.GetDataDocument<MyDataClass>()>(It.IsAny<Guid>()))
.Returns(() => new MyDataClass());
На самом деле не рекомендуется повторно использовать mocks, поэтому продолжайте и настройте макеты для фактического теста.
Ответ 3
У меня была аналогичная проблема, я выбрал против использования заглушки в этой ситуации, так как мне не нужны дополнения к тестируемому интерфейсу, чтобы потребовать немедленных изменений в тестовом коде. то есть добавление нового метода не должно нарушать мои существующие тесты.
Чтобы получить макетную работу, я добавил весь публичный тип в данную сборку во время выполнения.
//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 )
{
}