开发者

Using Expression Trees as an argument constraint

Can I use an Expression Tree as an argument constraint in a FakeIteasy CallTo assertion?

Given a method on an interface with the following signature:

开发者_JAVA百科interface IRepository<TEntity>
{
    TEntity Single(Expression<Func<TEntity, bool>> predicate);

Being called in code like so:

Flight flight = repository.Single(f => f.ID == id);

I have in mind a unit test doing something like this:

Expression<Func<Flight, bool>> myExpression = flight => flight.ID == 1;

A.CallTo(() => repository.Single(
                  A<Expression<Func<Flight, bool>>>.That.Matches(myExpression)))
                  .Returns(new Flight());

However this produces a warning: Try specifying type arguments explicitly.

I am currently having to use the Ignored property which is not ideal.


The "Matches"-method takes a lambda but you're trying to pass it the expression. What are you trying to say with the "Matches"-call? Are you matching on equality? In that case you'd just write:

A.CallTo(() => repository.Single(myExpression)).Returns(new Flight());

If you want to constrain the expression on something else you'd have to pass a predicate of the type: Func<Expression<Func<Flight, bool>>, bool> to the "Matches"-method.


Thanks Patrik,

Examining the expression was exactly what I needed to do, i.e. parse the expression (f => f.ID == id) and execute the Right side of the == to get its runtime value.

In code this looks like this:

A.CallTo(() => flightRepository.Single(A<Expression<Func<Flight, bool>>>.That
                .Matches(exp => Expression.Lambda<Func<int>>(((BinaryExpression)exp.Body).Right).Compile().Invoke() == 1)))
                .Returns(new Flight());

However I can't help thinking that there must be a more elegant way to achieve the same end. I'll leave that for another day though.

Thanks again, Michael McDowell


I had the same problem while attempting to assert an expression as an argument but I was using Moq. The solution should work for you though as well...

I give most of the credit to this answer to a similar question: Moq Expect On IRepository Passing Expression

It basically says you can do a ToString() on the expressions and compare them. It is kind of hacky but it only has one downside; the variables names in the lambda expression must match.

Here is an example...

    [Test]
    public void TestWhichComparesExpressions()
    {
        // setup
        _mockRepository.Setup(x => x.GetByFilter(MatchQuery())).Returns(new List<Record>());

        // execute
        var records = _service.GetRecordsByFilter();

        // assert
        Assert.IsNotNull(records);
        Assert.AreEqual(0, records.Count());
    }

    private static Expression<Func<DomainRecord, bool>> MatchQuery()
    {
        return MatchExpression(ServiceClass.QueryForTheRecords); // constant
    }

    // https://stackoverflow.com/questions/288413/moq-expect-on-irepository-passing-expression/1120836#1120836
    private static Expression<Func<DomainRecord, bool>> MatchExpression(Expression<Func<DomainRecord, bool>> expression)
    {
        return It.Is<Expression<Func<DomainRecord, bool>>>(e => e.ToString() == expression.ToString());
    }

I decided to put the expression into a constant on the class which used it which guaranteed it would be the same in the test if someone changed the lambda expressions's variable names.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜