I want to create a factory that will create commonly mocked objects for my unit tests. I've already managed to set up my tests so I can mock up a Linq2Sql DataContext and return an in memory table instead of hitting the database. I set it up like this:
_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
This gets tedious if the DataContext contains a lot of tables, so I thought a simple factory method that used reflection to get all the tables off the DataContext might help:
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;
}
So far I've figured out how to create the objects I need to setup for the Mock, but I just can't figure out how to dynamically call Moq's Setup() passing in the property names. I started looking at reflection to Invoke() Moq's Setup() method, but it got really ugly fast.
Does anyone have a simple way to dynamically call Setup() and Returns() like this?
Edit: Brian's answer got me there. Here's how it works:
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;
}
What you are looking for are Linq Expressions. Here is a sample of building a property accessory expression in action.
Using this class:
public class ExampleClass
{
public virtual string ExampleProperty
{
get;
set;
}
public virtual List<object> ExampleListProperty
{
get;
set;
}
}
The following tests demonstrate accessing its properties dynamically using the Linq.Expression
classes.
[TestClass]
public class UnitTest1
{
[TestMethod]
public void SetupDynamicStringProperty()
{
var dynamicMock = new Mock<ExampleClass>();
//Class type
var parameter = Expression.Parameter( typeof( ExampleClass ) );
//String rep of property
var body = Expression.PropertyOrField( parameter, "ExampleProperty" );
//build the lambda for the setup method
var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>( body, parameter );
dynamicMock.Setup( lambdaExpression ).Returns( "Works!" );
Assert.AreEqual( "Works!", dynamicMock.Object.ExampleProperty );
}
[TestMethod]
public void SetupDynamicListProperty_IntFirstInList()
{
var dynamicMock = new Mock<ExampleClass>();
var parameter = Expression.Parameter( typeof( ExampleClass ) );
var body = Expression.PropertyOrField( parameter, "ExampleListProperty" );
var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>( body, parameter );
var listOfItems = new List<object> { 1, "two", DateTime.MinValue };
dynamicMock.Setup( lambdaExpression ).Returns( listOfItems );
Assert.AreEqual( typeof( int ), dynamicMock.Object.ExampleListProperty[0].GetType() );
Assert.AreEqual( 1, dynamicMock.Object.ExampleListProperty[0] );
Assert.AreEqual( 3, dynamicMock.Object.ExampleListProperty.Count );
}
[TestMethod]
public void SetupDynamicListProperty_StringSecondInList()
{
var dynamicMock = new Mock<ExampleClass>();
var parameter = Expression.Parameter( typeof( ExampleClass ) );
var body = Expression.PropertyOrField( parameter, "ExampleListProperty" );
var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>( body, parameter );
var listOfItems = new List<object> { 1, "two" };
dynamicMock.Setup( lambdaExpression ).Returns( listOfItems );
Assert.AreEqual( typeof( string ), dynamicMock.Object.ExampleListProperty[1].GetType() );
Assert.AreEqual( "two", dynamicMock.Object.ExampleListProperty[1] );
Assert.AreEqual( 2, dynamicMock.Object.ExampleListProperty.Count );
}
}
EDIT
You are taking a step too far with this code. This code is creating a method with the signature of the lambda you want and then it's executing it (.Invoke). You are then trying to pass the result of the object (hence the compile error) into the setup for Moq. Moq will do the method execution and hookup for you once you tell it how to act (hence the lambda). If you use the lambda expression creation that I provided, it will build what you need.
var funcType = typeof (Func<>).MakeGenericType(new Type[] {TableType, typeof(object)});
var lambdaMethod = typeof (Expression).GetMethod("Lambda");
var lambdaGenericMethod = lambdaMethod.MakeGenericMethod(funcType);
var lambdaExpression = lambdaGenericMethod.Invoke(body, parameter);
//var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>(body, parameter); // FOR REFERENCE FROM BRIAN'S CODE
instance.Setup(lambdaExpression).Returns(inMemoryTable);
Do this instead
var parameter = Expression.Parameter( TableType );
var body = Expression.PropertyOrField( parameter, "PutYourPropertyHere" );
var lambdaExpression = Expression.Lambda<Func<ExampleClass, object>>( body, parameter );
instance.Setup(lambdaExpression).Returns(inMemoryTable);
EDIT
Took a stab at correcting the GetMockContext. Please note the few changes (I marked each line). I think this is closer. I am wondering, does InMemoryTable inherit from DataContext? If not, the method signature will be incorrect.
public static object GetMockContext<T>() where T: DataContext
{
Type contextType = typeof (T);
var instance = new Mock<T>(); //Updated this line
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; //had to change the method signature because the inMemoryTable is not of type DataContext. Unless InMemoryTable inherits from DataContext?
}
I hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With