2011-06-23 17 views
4

単体テスト用の一般的なオブジェクトを作成するファクトリを作成したいとします。私はすでにLinq2Sql DataContextをモックアップして、データベースにヒットする代わりにメモリテーブルを返すように、私のテストを設定することができました。 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のオフすべてのテーブルを取得するためにリフレクションを使用し、単純なファクトリメソッドが役立つかもしれないと思った:

私はこのようにそれを設定しました
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; 
} 

これまで、私はMock用にセットアップする必要があるオブジェクトを作成する方法を理解しましたが、プロパティ名を渡す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; 
    } 
+0

ラムダメソッドの3行は、メソッドを作成してから呼び出します。私の答えの例を使ってラムダメソッドを作成し、setupメソッドに渡す必要があります。 –

+0

編集していただきありがとうございます。最初のコードでは、特定のコンテキスト(ContactsDataContext)を持ち、汎用コンテキスト(DataContext)を使用してそのコンテキストに移行します。この問題は、テンプレート化されたクラスに存在するプロパティとData Contextのモックを混在させることです。私はいくつかのコードで私の答えを更新するつもりです。 –

+0

ああ、あなたのコメントは私のためにそれを分類したと思います。モックの代わりにモックを作成する必要がありました。できます! –

答えて

6

あなたが探しているものはLinq式です。以下は、実際にプロパティアクセサリー式を構築するサンプルです。このクラス

public class ExampleClass 
{ 
    public virtual string ExampleProperty 
    { 
     get; 
     set; 
    } 

    public virtual List<object> ExampleListProperty 
    { 
     get; 
     set; 
    } 
} 

次のテストを使用して

それが動的にLinq.Expressionクラスを使用してプロパティのアクセスを示しています。あなたはこのコードをあまりにも遠くのステップを取っている

[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

。このコードは、あなたが望むラムダのシグネチャを持つメソッドを作成し、それを実行しています(.Invoke)。次に、オブジェクトの結果(したがってコンパイルエラー)をMoqの設定に渡そうとしています。 Moqはメソッドの実行と接続を行います(これはラムダです)。私が提供したラムダ式の作成を使用すると、必要なものが構築されます。

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); 

代わりにこれを行う

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

GetMockContextの補正で刺しを取りました。いくつかの変更点に注意してください(各行にマークを付けました)。私はこれがより近いと思う。 InMemoryTableはDataContextから継承していますか?そうでない場合、メソッドのシグネチャは正しくありません。

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? 
} 

私はこれが役立つことを願っています!

+0

それは私にもう少し近づくが、私が知っている限り、リフレクションからのタイプであるならば、Expressionを動的に作成することにまだ苦労している。元の質問に私の編集を参照してください。 –

+0

Func のようなラムダのセンテンスの関数を作成してみてください。私はそれがあなたが行く必要があるところにあなたを得ると思います。仮定しています.net 4.0 ... –

+0

私は 'System.Linq.Expressions.Expression >'から 'System.Linq.Expressions.Expression'に変換できません。 .Linq.DataContext >> ' –

関連する問題