式ツリーの単体テスト
-
03-07-2019 - |
質問
最近、式ツリーを構築する必要があるため、次のようにTestメソッドを記述しました...
/// <summary>
///
/// </summary>
[TestMethod()]
[DeploymentItem("WATrust.Shared.Infrastructure.dll")]
public void BuildForeignKeysContainsPredicate_shoud_build_contains_predicate()
{
RemoteEntityRefLoader_Accessor<ReferencedEntity> target = CreateRemoteEntityRefLoader_Accessor();
List<object> foreignKeys = new List<object>() { 1, 2, 3, 4 };
Expression<Func<ReferencedEntity, bool>> expected = (ReferencedEntity referencedEntity) => foreignKeys.Contains(referencedEntity.Id);
Expression<Func<ReferencedEntity, bool>> actual;
actual = target.BuildForeignKeysContainsPredicate(foreignKeys, "Id");
Assert.AreEqual(expected.ToString(), actual.ToString());
}
ついに<!> quot; BuildForeignKeysContainsPredicate <!> quot;を取得したときメソッドが動作するテストに合格することはありませんでした...メソッドは次のとおりです:
/// <summary>
///
/// </summary>
/// <param name="foreignKeys"></param>
/// <returns></returns>
private Expression<Func<TReferencedEntity, bool>> BuildForeignKeysContainsPredicate(List<object> foreignKeys, string primaryKey)
{
Expression<Func<TReferencedEntity, bool>> result = default(Expression<Func<TReferencedEntity, bool>>);
try
{
ParameterExpression entityParameter = Expression.Parameter(typeof(TReferencedEntity), "referencedEntity");
ConstantExpression foreignKeysParameter = Expression.Constant(foreignKeys, typeof(List<object>));
MemberExpression memberExpression = Expression.Property(entityParameter, primaryKey);
Expression convertExpression = Expression.Convert(memberExpression, typeof(object));
MethodCallExpression containsExpression = Expression.Call(foreignKeysParameter
, "Contains", new Type[] { }, convertExpression);
result = Expression.Lambda<Func<TReferencedEntity, bool>>(containsExpression, entityParameter);
}
catch (Exception ex)
{
throw ex;
}
return result;
}
しかし、テストは毎回失敗し、ラインを切り替えましたAssert.AreEqual(expected, actual);
これに:Assert.AreEqual(expected.ToString(), actual.ToString());
ToStringメソッドの結果を見ると異なるため、なぜ失敗するのか理解しています。
Assert.AreEqual failed.
Expected:<referencedEntity => value(Shared.Infrastructure.Test.RemoteEntityRefLoaderTest+<>c__DisplayClass13).foreignKeys.Contains(Convert(referencedEntity.Id))>.
Actual :<referencedEntity => value(System.Collections.Generic.List`1[System.Object] ) .Contains(Convert(referencedEntity.Id))>.
理由がわからない...ユニットテストの式に関する一般的なヒントや、特定のテストに合格するための提案はありますか?
ありがとう...
解決
投稿したコードに基づいて、
- 期待される値は匿名のデリゲート/メソッドです。 CLRは、その場でメソッドを追加するために、舞台裏でいくつかの魔法を実行します。場合には、アノン。メソッドは特定のローカル変数を使用し、CLRはこれらの値に設定されたフィールドを持つ新しいクラスを作成し、その中に新しいanonメソッドを(メソッドがローカルvar値にアクセスできるように)作成します。これが..c_DisplayClass13であり、コンパイラが奇妙な名前を付けて、ユーザー定義のメソッドと衝突しないようにします。
- メソッドによって返される実際の値は
Expression<T>
です。
したがって、これら2つの間の等価性チェックは失敗します。両方から返されるコレクションの要素を比較する必要があります。だから私は提案したい..期待値と実際の値の両方をリスト(またはより良いデータ構造)に変換してから、コレクションパラメータを取るNUnitのアサートの1つを呼び出す。
更新:式ツリーを読んでもらいました。そのために+1。
答えを変更します-ハックアンドアサートを介して式ツリーを比較すると、脆弱なテストになります(たとえば、MSが将来式ツリーの内部構造を変更する場合)
式ツリーはFunc<TInput,TResult)
に似た結果に評価される単なるコードブロックです(したがって、私のテストでは、期待されるコードブロックと実際のコードブロックに同じ入力を与え、同じ出力を提供するかどうかを確認します)。
したがって、あなたのテストに対する私の主張は
Assert.AreEqual(expected.Compile().Invoke(inputEntity),
actual.Compile().Invoke(inputEntity));