单元测试表达式树

byt*_*der 11 c# linq testing lambda unit-testing

我最近需要构建一个Expression树,所以我写了一个这样的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());
    }
Run Code Online (Sandbox Code Playgroud)

当我最终得到"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;
    }
Run Code Online (Sandbox Code Playgroud)

但是测试每次都失败了,我把线切换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))>.
Run Code Online (Sandbox Code Playgroud)

我只是不明白为什么......是否有任何人有关于单元测试表达式的一般提示和建议如何让我的特定测试通过?

谢谢...

Gis*_*shu 16

根据您发布的代码,

  • 预期值是匿名委托/方法.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));
Run Code Online (Sandbox Code Playgroud)