单元测试表达式树

11

最近,我需要构建一个表达式树,所以我编写了以下测试方法...

    /// <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());
    }

当我最终让"BuildForeignKeysContainsPredicate"方法工作起来后,我从未能够使测试通过... 这是该方法:

    /// <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))>.

我就是不明白为什么...有没有任何人对单元测试表达式有一般的建议,并且能给出如何使我的具体测试通过的建议?

谢谢...

1个回答

16

根据您发布的代码,

  • 期望值是一个匿名委托/方法。CLR在后台执行一些魔术来动态添加方法。如果匿名方法使用某些局部变量,则CLR将创建一个新类,并将其字段设置为这些值,其中包含新的匿名方法(以便该方法可以访问局部变量值)。因此这就是您的..c_DisplayClass13,编译器给定了奇怪的名称,以避免与用户定义的方法冲突。
  • 实际返回的值是一个Expression<T>

因此,这两者之间的相等性检查失败了。您需要比较它们返回的集合的元素。所以我建议... 将期望值和实际值都转换为List(或更好的数据结构),然后调用 Nunit 的断言函数之一,它接受集合参数。

更新:你让我去学习了表达式树。+1
我要改变我的答案-通过修改和断言来比较表达式树将导致一个脆弱的测试(例如,如果Microsoft在将来更改表达式树的内部结构),
表达式树只是代码块(正如我现在发现的那样),它们评估结果类似于Func<TInput,TResult) - 因此,我的测试是为期望值和实际值提供相同的输入,并查看它们是否产生相同的输出。 因此,我的断言如下:

Assert.AreEqual(expected.Compile().Invoke(inputEntity), 
                actual.Compile().Invoke(inputEntity));

你是说要比较每个表达式中的每个节点吗? - bytebender
我喜欢它...我同意。如果我开始比较表达式的部分和微软进行一些更改,突然之间可能会有很多测试失败。我喜欢你的策略。谢谢! - bytebender

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接