Why this mock with Expression<Func<T,bool> is not matching?

橙三吉。 提交于 2019-12-06 10:41:28

If it is needed to test delegates I usually apply them to any test object. In order to test PersonService I'd rather use the following code that contains examples of two tests.

[TestClass]
public class UnitTest2
{
    private Mock<IRepository> moqRepository;
    private PersonService service;

    [TestInitialize]
    public void TestInitialize()
    {
        moqRepository = new Mock<IRepository>();
        service = new PersonService(moqRepository.Object);
    }

    [TestMethod]
    public void TestMethod3()
    {
        // arrange
        var persons = new List<Person>
        {
            new Person { Age = 0 },
            new Person { Age = 1 },
            new Person { Age = 17 },
            new Person { Age = 18 },
            new Person { Age = 19 },
            new Person { Age = 100 }
        };
        moqRepository
            .Setup(x => x.GetForExpression(It.IsAny<Expression<Func<Person, bool>>>()))
            .Returns<Expression<Func<Person, bool>>>(expr => persons.Where(expr.Compile()).ToList());

        // act
        var result = service.GetAllPersonsWith18More();

        // assert
        moqRepository.VerifyAll();
        result.Should().BeEquivalentTo(persons.Where(x => x.Age > 18));
    }

    [TestMethod]
    public void TestMethod4()
    {
        // arrange
        Expression<Func<Person, bool>> criteria = x => x.Age > 18;
        moqRepository
            .Setup(x => x.GetForExpression(It.IsAny<Expression<Func<Person, bool>>>()))
            .Returns(new List<Person>())
            .Callback<Expression<Func<Person, bool>>>(expr =>
            {
                var persons = new List<Person>
                {
                    new Person { Age = 0 },
                    new Person { Age = 1 },
                    new Person { Age = 17 },
                    new Person { Age = 18 },
                    new Person { Age = 19 },
                    new Person { Age = 100 }
                };
                persons.Where(expr.Compile()).Should().BeEquivalentTo(persons.Where(criteria.Compile()));
            });

        // act
        service.GetAllPersonsWith18More();

        // assert
        moqRepository.VerifyAll();
    }
}

Expressions are not comparable, so == will return false even if expression trees exactly matches:

int criteria = 5;
Expression<Func<int, bool>> criteria1 = y => y == criteria;
Expression<Func<int, bool>> criteria2 = y => y == criteria;
System.Diagnostics.Debug.WriteLine(criteria1 == criteria2); // false

As workaround, you can call expression.ToString() and compare string representations: Comparing Simple Lambda Expressions With Moq.

Chris Mantle

Expressions can't be compared like that. If you want to match expressions in that much detail, you need to take the expression passed to your mock and parse it's tree (as outlined in this answer). The result would look something like this (where FuncTest.FuncEqual can be found in the previous answer):

moqRepository
    .Setup(x => x.GetForExpression(ExpressionMatches(criteria))
    .Returns(new List<Person>());

// ...

public static Expression<Func<TSource, TValue>> ExpressionMatches(Expression<Func<TSource, TValue>> expr)
{
    return Match.Create<Expression<Func<TSource, TValue>>(actualExpr => FuncTest.FuncEqual(expr, actualExpr));
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!