Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I convert a Predicate<T> to an Expression<Predicate<T>> to use with Moq?

Tags:

c#

lambda

linq

moq

Please help this Linq newbie!

I'm creating a list inside my class under test, and I would like to use Moq to check the results.

I can easily put together a Predicate which checks the results of the list. How do I then make that Predicate into an Expression?

var myList = new List<int> {1, 2, 3};

Predicate<List<int>> myPredicate = (list) => 
                  {
                      return list.Count == 3; // amongst other stuff
                  };

// ... do my stuff

myMock.Verify(m => m.DidStuffWith(It.Is<List<int>>( ??? )));

??? needs to be an Expression<Predicate<List<int>>> if you can get your head round that many generics. I've found answers which do this the other way round and compile an Expression into a Predicate. They're not helping me understand Linq any better, though.

EDIT: I've got it working with methods; with expressions; I would just like to know if there's any way to do it with a lambda with a body - and if not, why not?

like image 512
Lunivore Avatar asked Oct 21 '10 14:10

Lunivore


2 Answers

Change:

Predicate<List<int>> myPredicate = (list) => list.Count == 3;

To:

Expression<Predicate<List<int>>> myPredicate = (list) => list.Count == 3;

The compiler does the magic for you. With some caveats1, any lambda dealing only with expressions (no blocks) can be converted into an expression tree by wrapping the delegate type (in this case Predicate<List<int>>) with Expression<>. As you noted, you could then invert it yet again by calling myPredicate.Compile().

1 For example, async lambdas (i.e. async () => await Task.Delay(1);) cannot be converted to an expression tree.

Update: You simply cannot use the compiler to arrive at the expression tree you want if it includes statements. You'll have to build up the expression tree yourself (a lot more work) using the static methods in Expression. (Expression.Block, Expression.IfThen, etc.)

like image 54
Kirk Woll Avatar answered Sep 28 '22 02:09

Kirk Woll


Kirk Woll's answer directly addresses your question, but consider the fact that you can use the Callback method on a Setup of a void method to handle the parameters which were passed in on invocation. This makes more sense to me since you're already having to build a method to validate the list anyway; it also gives you a local copy of the list.

//what is this list used for?
var myList = new List<int> {1, 2, 3};

List<int> listWithWhichStuffWasDone = null;
//other setup

myMock.Setup(m => m.DoStuffWith(It.IsAny<List<int>>()).
    Callback<List<int>>(l => listWithWhichStufFWasDone = l);

objectUnderTest.MethodUnderTest();

myMock.Verify(m => m.DoStuffWith(It.IsAny<List<int>>()));
Validate(listWithWhichStuffWasDone);
like image 40
Matt Mills Avatar answered Sep 28 '22 03:09

Matt Mills