关于c#:模拟任何给定类型参数的泛型方法调用

Mocking generic method call for any given type parameter

我有一个界面

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

我想以某种方式模拟它,它只会返回给定类型的新实例,而不管确切的类型,如:

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

(当然不起作用,因为我不能只给moq提供任何类型参数,我不知道必须返回哪种类型。

关于这个的任何想法?


而不是使用模拟,也许你的情况会更好地使用存根。

1
2
3
4
5
6
7
public class StubDataProvider : IDataProvider
{
    public T GetDataDocument< T >(Guid document) where T : class, new()
    {
        return new T();
    }
}

如果你真的需要一个模拟(所以你可以验证GetDataDocument被调用)。 而不是试图与Mocking框架搏斗,有时更容易创建一个Mock类正确。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();
    }
}

而且比你的测试:

1
2
3
4
bool wasCalled = false;
IDataProvider dataProvider = new MockDataProvider(() => { wasCalled = true; });
var aTable = dataProvider.GetDataDocument<ATable>(new Guid());
Debug.Assert(wasCalled);


对于你要使用这个模拟的特定测试,你可能知道T会是什么,对吧?

只需为此设置模拟:

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

无论如何都不建议重复使用模拟器,因此请继续设置模拟器以进行实际测试。


我有一个类似的问题,我选择在这种情况下使用存根,因为我不希望添加到被测试的接口需要立即更改测试代码。 即添加新方法不应该破坏我现有的测试。

为了使模拟工作,我在运行时添加了给定程序集中的所有公共类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//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 )
{
}