单元测试表达式树

时间:2009-05-04 18:09:07

标签: c# linq unit-testing testing lambda

我最近需要构建一个表达式树,所以我写了一个像这样的测试方法......

    /// <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 个答案:

答案 0 :(得分:15)

根据您发布的代码,

  • 期望值是匿名委托/方法。 CLR在场景背后做了一些魔术来动态添加方法。如果是anon。方法使用某些局部变量,CLR创建一个新的类,其字段设置为这些值,其中包含新的anon方法(以便该方法可以访问本地var值)。这就是你的..c_DisplayClass13,编译器给出了奇怪的名称,这样它就不会与用户定义的方法冲突。
  • 您的方法返回的实际值为Expression<T>

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

更新:您让我阅读表达式树。为此+1 我将改变我的答案 - 通过hack-and-assert比较表达式树将导致脆弱的测试(例如,如果MS在将来更改表达式树的内部结构)
表达式树只是代码块(我现在发现)评估结果类似于Func<TInput,TResult) - 所以我的测试是给出预期的和实际的代码块相同的输入,看看它们是否提供相同的输出。  所以我的测试断言是

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