我想创建一个工厂,为我的单元测试创建通常使用的模拟对象。我已经设置了我的测试,所以我可以模拟一个Linq2Sql DataContext并返回一个内存表而不是命中数据库。我把它像这样:在运行时动态调用Moq Setup()
_contactsTable = new InMemoryTable<Contact>(new List<Contact>());
_contactEmailsTable = new InMemoryTable<ContactEmail>(new List<ContactEmail>());
// repeat this for each table in the ContactsDataContext
var mockContext = new Mock<ContactsDataContext>();
mockContext.Setup(c => c.Contacts).Returns(_contactsTable);
mockContext.Setup(c => c.ContactEmails).Returns(_contactEmailsTable);
// repeat this for each table in the ContactsDataContext
这得到乏味,如果在DataContext中含有大量的表,所以我想使用反射来得到一个简单的工厂方法关闭DataContext的所有表可能会有所帮助:
public static DataContext GetMockContext(Type contextType)
{
var instance = new Mock<DataContext>();
var propertyInfos = contextType.GetProperties();
foreach (var table in propertyInfos)
{
//I'm only worried about ITable<> now, otherwise skip it
if ((!table.PropertyType.IsGenericType) ||
table.PropertyType.GetGenericTypeDefinition() != typeof (ITable<>)) continue;
//Determine the generic type of the ITable<>
var TableType = GetTableType(table);
//Create a List<T> of that type
var emptyList = CreateGeneric(typeof (List<>), TableType);
//Create my InMemoryTable<T> of that type
var inMemoryTable = CreateGeneric(typeof (InMemoryTable<>), TableType, emptyList);
//NOW SETUP MOCK TO RETURN THAT TABLE
//How do I call instance.Setup(i=>i.THEPROPERTYNAME).Returns(inMemoryTable) ??
}
return instance.Object;
}
到目前为止,我已经想出了如何创建我需要为模拟设置的对象,但我无法弄清楚如何动态调用传递属性名称的Moq的Setup()方法。我开始考虑对Invoke()Moq的Setup()方法的反射,但它的速度真的很难看。
有没有人有一个简单的方法来动态调用这样的Setup()和Returns()?
编辑:布赖恩的答案让我在那里。以下是它的工作方式:
public static DataContext GetMockContext<T>() where T: DataContext
{
Type contextType = typeof (T);
var instance = new Mock<T>();
var propertyInfos = contextType.GetProperties();
foreach (var table in propertyInfos)
{
//I'm only worried about ITable<> now, otherwise skip it
if ((!table.PropertyType.IsGenericType) ||
table.PropertyType.GetGenericTypeDefinition() != typeof(ITable<>)) continue;
//Determine the generic type of the ITable<>
var TableType = GetTableType(table);
//Create a List<T> of that type
var emptyList = CreateGeneric(typeof(List<>), TableType);
//Create my InMemoryTable<T> of that type
var inMemoryTable = CreateGeneric(typeof(InMemoryTable<>), TableType, emptyList);
//NOW SETUP MOCK TO RETURN THAT TABLE
var parameter = Expression.Parameter(contextType);
var body = Expression.PropertyOrField(parameter, table.Name);
var lambdaExpression = Expression.Lambda<Func<T, object>>(body, parameter);
instance.Setup(lambdaExpression).Returns(inMemoryTable);
}
return instance.Object;
}
使用lambda方法的三条线正在制作一个方法,然后调用它。您需要在我的答案中使用示例创建lambda方法并传递给安装方法。 –
感谢您的编辑。在您提到的具有特定上下文(ContactsDataContext)的第一部分代码中,您希望将其迁移到使用通用上下文(DataContext)。这样做的问题在于,您将DataContext的模拟与模板类上存在的属性混合在一起。我将用更多的代码更新我的答案。 –
啊,我想你的评论为我排序。我需要在顶部创建一个模拟而不是模拟。有用! –