模拟任何给定类型参数的泛型方法调用
Posted
技术标签:
【中文标题】模拟任何给定类型参数的泛型方法调用【英文标题】:Mocking generic method call for any given type parameter 【发布时间】:2011-07-15 17:23:45 【问题描述】:我有一个界面
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());
(这当然行不通,因为我不能只给起订量任何类型的参数,而且我不知道必须返回哪种类型。
对此有什么想法吗?
【问题讨论】:
【参考方案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());
不建议重用模拟,所以继续为手头的实际测试设置模拟。
【讨论】:
不,重点是,我不知道确切的类型,我知道会使用几种类型,但是我懒得为它们都写不同的设置子句:) 还有一种情况,被模拟的方法使用的是被模拟的程序集内部的类型定义,在定义设置时不能使用。【参考方案3】:Moq 4.13 或更高版本您可以使用
It.IsAnyType
— 匹配任何类型
It.IsSubtype<T>
— 匹配 T 和 T 的正确子类型
It.IsValueType
— 仅匹配值类型
要从泛型参数的值中获取值或对原始方法进行一些其他操作,可以使用InvocationAction
或InvocationFunc
的IInvocation
参数
setup.Callback(new InvocationAction(invocation => ...))
setup.Returns(new InvocationFunc(invocation => ...))
这是一个例子:
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);
));
【讨论】:
太棒了!顺便说一句,它也适用于接口。【参考方案4】:我遇到了类似的问题,在这种情况下我选择不使用存根,因为我不希望对正在测试的接口进行添加以要求立即更改测试代码。即添加新方法不应破坏我现有的测试。
为了使模拟工作,我在运行时在给定程序集中添加了所有公共类型。
//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 )
【讨论】:
以上是关于模拟任何给定类型参数的泛型方法调用的主要内容,如果未能解决你的问题,请参考以下文章