6

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

4

2 回答 2

4

披露- VasilisP 和我昨天聊了一点。

在某种程度上,这并不是一个真正的 FakeItEasy 问题。您在调用中设置参数匹配器的方法A.CallTo是合理的。问题是您提供的用于匹配谓词的 lambda 不起作用。这将问题归结为“我如何判断一个表达式是否是我想要的?”。

还有其他 StackOverflow 问题会提出与此类似的问题,例如

其中一种方法可能对您有用。

但是,您看到的异常的直接原因是传入的谓词不是 a BinaryExpression,而是 a MethodCallExpression。你可以考虑改变你的测试来解决这个问题,并遵循它引导你的路径。

我填写了一些类定义并将匹配器提取到该函数中,并且至少能够在谓词中找到 dateArgument:

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
}

不过,总的来说,我会警告不要像这样进行测试——这对我来说似乎有点脆弱。当然,您可能有充分的理由采用这种方法。但是,如果它适合您,另一种方法可能是捕获谓词,然后通过使其与已知的候选对象列表运行来询问它。如果它以您想要的方式过滤,那么它就会通过。这样,如果有人以仍然有效的方式更改传入的谓词,也许通过将运算符切换到<左侧的日期,测试仍然有效。我只是把它作为另一种选择。

于 2014-01-29T23:06:23.283 回答
3

对不起,我应该早点回答这个问题。确实,Blair Conrad 和我进行了交谈,他帮助我了解了如何更好地测试谓词。根据他的建议,我提出了以下解决方案。

在我的测试中,我创建了一个辅助表达式提取器,如下所示:

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

然后在我的测试中,我可以这样断言:

//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);
于 2014-04-22T10:16:44.420 回答