How to test for a Match with FakeItEasy on a predicate call?

梦想与她 提交于 2019-11-29 07:42:48
Blair Conrad

Disclosure - VasilisP and I chatted a little about this yesterday.

In a way, this isn't really a FakeItEasy problem. Your approach for setting up an argument matcher within an A.CallTo call is sound. The problem is that the lambda you supplied to match the predicate is not working. This brings the question down to the "how can I tell if an expression is what I want it to be?".

There are other StackOverflow questions that ask questions similar to this, such as

One of those approaches may work for you.

However, the immediate cause of the exception you see is that the passed-in predicate isn't a BinaryExpression, it's a MethodCallExpression. You could consider altering your test to account for that and follow the path where it leads you.

I filled in some class definitions and extracted the matcher to this function and was able to at least locate the dateArgument in the predicate:

public bool IsPredicateGood(Expression<Func<CrossReferenceRelationshipEF, bool>> predicate)
{
    var typedPredicate = (MethodCallExpression) predicate.Body;
    var innerPredicate = ((LambdaExpression)typedPredicate.Arguments[1]).Body;
    var dateArgument = ((BinaryExpression) innerPredicate).Right;
    return dateArgument != null; // not a real test yet, but you could adapt
}

In general, though, I'd warn against testing quite like this - it seems a little fragile to me. Of course, you may have a good reason for this approach. But if it suits you, another way to go may be to just capture the predicate and then interrogate it by having it run against a known list of candidate objects. If it filters the way you want, then it passes. That way if someone changes the passed-in predicate in a way that would still work, perhaps by switching the operator to a < with the date on the left, the test would still work. I just throw that out as another option.

Sorry I should have answered this earlier. It is true that Blair Conrad and I had a chat and he helped me understand how to test the predicates better. Based on his recommendation I came up with the following solution.

In my tests I created a helper Expression extractor show below:

private static string ExpressionExtractor(Expression<Func<CrossReferenceRelationshipEF, bool>> predicate)
{
    var expression = ((BinaryExpression) ((LambdaExpression) ((MethodCallExpression) predicate.Body).Arguments[1]).Body);
    var value = Expression.Lambda<Func<object>>(Expression.Convert(expression.Right, typeof (object))).Compile().Invoke();

    return value.ToString();
}

And then in my tests I could do my assert like this:

//Assert        
A.CallTo(() => crossReferenceRelationshipRepositoryMock.SearchFor(A<Expression<Func<CrossReferenceRelationshipEF, bool>>>.That
    .Matches(exp => ExpressionExtractor(exp) == "20/01/2014 14:06:55")))
    .MustHaveHappened(Repeated.Exactly.Twice);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!