Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Testing Expression Trees

I recently need to build a Expression tree so I wrote a Test method like so...

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

When I finally got the "BuildForeignKeysContainsPredicate" method working I could never get teh test to pass... Here is the method:

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

But the test fails every time, I switched the line Assert.AreEqual(expected, actual); to this: Assert.AreEqual(expected.ToString(), actual.ToString()); I understand why it is failing because when you look at the results of the ToString method they are different.

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

I just don't understand why... Does any one have general tips on unit testing expressions and suggestions how to get my specific test to pass?

Thanks...

like image 458
bytebender Avatar asked May 04 '09 18:05

bytebender


1 Answers

Based on the code you've posted,

  • the expected value is an anonymous delegate/method. The CLR does some magic behind the scene to add a method on the fly. In case the anon. method uses certain local variables, the CLR creates a new class with fields set to these values, with the new anon method inside it (so that the method can access the local var values). So that's your ..c_DisplayClass13, with a compiler given weird name so that it doesn't clash with user-defined methods.
  • The actual value returned by your method is an Expression<T>.

And hence.. the equality check between these two fails. You need to compare the elements of the collection returned by both of them. So I'd suggest.. convert both the expected and actual values to Lists (or a better data structure) and then invoke one of NUnit's asserts that take collection parameters.

Update: You got me to read up on Expression Trees. +1 for that.
I'm going to change my answer - Comparing the Expression trees via hack-and-assert would lead to a fragile test (e.g. if MS changes the internal structure of an expression tree in the future)
Expression trees are just code blocks (as I found out now) that evaluate to a result similar to a Func<TInput,TResult) - so my test would be to give the expected and actual code blocks the same input and see if they deliver the same output. So my assert for your test would be

Assert.AreEqual(expected.Compile().Invoke(inputEntity), 
                actual.Compile().Invoke(inputEntity));
like image 168
Gishu Avatar answered Sep 22 '22 07:09

Gishu