I have the following call in my code:
var dbResults = new List<CrossReferenceRelationshipEF>();
dbResults = dateTimeFilter == null
? new List<CrossReferenceRelationshipEF>(
CrossReferenceRelationshipRepository.GetAll()
.ToList().OrderBy(crr => crr.ToPartner))
: new List<CrossReferenceRelationshipEF>(
CrossReferenceRelationshipRepository.SearchFor(
crr => crr.HistoricEntries
.Any(he => he.ModifiedDatetime > dateTimeFilter))
.ToList().OrderBy(crr => crr.ToPartner));
and I am trying to use FakeItEasy to verify that when the dateTimeFilter
has a value, the SearchFor(…)
is being called within my repository with the correct Function.
So my test looks something like this:
A.CallTo(() => crossReferenceRelationshipRepositoryMock.SearchFor(A<Expression<Func<CrossReferenceRelationshipEF,bool>>>.That
.Matches(exp => Expression.Lambda<Func<DateTime>>(((BinaryExpression)exp.Body).Right).Compile().Invoke() == filterByDate)))
.MustHaveHappened(Repeated.Exactly.Once);
Which is not correct.
What would be a way to test the whether or not I am calling SearchFor(…)
with the correct expression?
crr => crr.HistoricEntries.Any(he => he.ModifiedDatetime > dateTimeFilter)
The actual value being passed into SearchFor(…)
is DateTime.MinValue
so I changed my assertion to:
A.CallTo(() => crossReferenceRelationshipRepositoryMock.SearchFor(A<Expression<Func<CrossReferenceRelationshipEF, bool>>>.That
.Matches(exp => Expression.Lambda<Func<DateTime>>(((BinaryExpression)exp.Body).Right).Compile().Invoke() == DateTime.MinValue)))
.MustHaveHappened(Repeated.Exactly.Once);
which is failing and the exception I am getting is
System.InvalidCastException:
Unable to cast object of type 'System.Linq.Expressions.MethodCallExpressionN'
to type 'System.Linq.Expressions.BinaryExpression'.
and I am not sure what I am doing wrong...
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With