Moqを使用してデータセットのモックを作成しています。
単体テストを簡単にするデータベースの代わりにメモリ内ストレージを使用できるようにする小さなヘルパークラスを作成しました。このようにして、モックデータセットにアイテムを追加および削除できます。これにより、挿入および削除サービス呼び出しをテストできます。
モックのセットアップ中に、次のような行があります
this.Setup(i => i.AcademicCycles).Returns(mockStore.GetList<AcademicCycle>());
私のモックにはたくさんのプロパティがあるので、リフレクションを使用してこのセットアップ手順を実行したいと思います。リフレクションを介して動作するプロセスのReturns
部分に成功しましたが、Setup
へのラムダメソッドで立ち往生しています。
Setup
は
Expression<Func<GoalsModelUnitOfWork, IQueryable<AcademicCycle>>>
に対応するi => i.AcademicCycles
これを動的に作成したいと思います。リフレクションを使用すると、次のようになります。
プロパティの名前:「AcademicCycles」
タイプIQueryable<AcademicCycle>
タイプAcademicCycle
ラムダステートメントにi
のインスタンスもあります。これはGoalsModelUnitOfWork
です。
式を動的に作成するコードは次のようになります。
_ParameterExpression parameter = Expression.Parameter(typeof (GoalsModelUnitOfWork), "i");
MemberExpression property = Expression.Property(parameter, "AcademicCycles");
var queryableType = typeof (IQueryable<>).MakeGenericType(typeof (AcademicCycle));
var delegateType = typeof (Func<,>).MakeGenericType(typeof (GoalsModelUnitOfWork), queryableType);
var yourExpression = Expression.Lambda(delegateType, property, parameter);
_
結果は目的の型になりますが、問題は、Expression.Lambda()
の戻り値の型がLambdaExpression
であり、_Expression<Func<...>>
_に型キャストを実行してそれを渡すことができないことです。 Func
の一般的な型パラメーターがわからないため、セットアップ関数のパラメーター。したがって、リフレクションによってSetup
メソッドも呼び出す必要があります。
_this.GetType().GetMethod("Setup", yourExpression.GetType()).Invoke(this, yourExpression);
_
このメソッドはラムダ式を構築する必要があります。リフレクションによってSetupメソッドを呼び出すため、強く型付けされたラムダ式は必要ありません。 Invoke
を呼び出すときに、オブジェクト配列の一部として渡します。
public LambdaExpression PropertyGetLambda(string parameterName, Type parameterType, string propertyName, Type propertyType)
{
var parameter = Expression.Parameter(parameterType, parameterName);
var memberExpression = Expression.Property(parameter, propertyName);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
return lambdaExpression;
}
実際にパラメータ名は必要ないと思います。私がそれについて正しければ、あなたは少し単純化することができます:
public LambdaExpression PropertyGetLambda(Type parameterType, string propertyName, Type propertyType)
{
var parameter = Expression.Parameter(parameterType);
var memberExpression = Expression.Property(parameter, propertyName);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
return lambdaExpression;
}
私はそれをクラックすることに決め、この神のひどいコードになってしまいました。
私はリフレクションの専門家ではありません。これは、何かを機能させるための最初の試みにすぎません。私は、人々が他にどのようなアプローチを取っているのか、またはいずれかの反射ラッパーライブラリがこれをより良くすることができるかどうかに非常に興味があります。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Moq;
using Xunit;
namespace MyExample
{
public class Tests
{
[Fact]
public void Test()
{
Dictionary<Type, object> data = new Dictionary<Type, object>
{
{ typeof(IQueryable<Cycle>), new List<Cycle> { new Cycle { Name = "Test" } }.AsQueryable() },
{ typeof(IQueryable<Rider>), new List<Rider> { new Rider { Name = "1"}, new Rider { Name = "2" } }.AsQueryable() }
};
var mock = new Mock<IDataContext>();
var setup = mock.GetType().GetMethods().Single(d => d.Name == "Setup" && d.ContainsGenericParameters);
var param = Expression.Parameter(typeof(IDataContext), "i");
foreach (var property in typeof(IDataContext).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
// Build lambda
var ex = Expression.Lambda(Expression.MakeMemberAccess(param, property), param);
// Get generic version of the Setup method
var typedSetup = setup.MakeGenericMethod(property.PropertyType);
// Run the Setup method
var returnedSetup = typedSetup.Invoke(mock, new[] { ex });
// Get generic version of IReturns interface
var iReturns = typedSetup.ReturnType.GetInterfaces().Single(d => d.Name.StartsWith("IReturns`"));
// Get the generic Returns method
var returns = iReturns.GetMethod("Returns", new Type[] { property.PropertyType });
// Run the returns method passing in our data
returns.Invoke(returnedSetup, new[] { data[property.PropertyType] });
}
Assert.Equal(1, mock.Object.Cycles.Count());
}
}
public class Cycle
{
public string Name { get; set; }
}
public class Rider
{
public string Name { get; set; }
}
public interface IDataContext
{
IQueryable<Cycle> Cycles { get; set; }
IQueryable<Rider> Riders { get; set; }
}
}