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

2

6

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?

Bulldoze answered 21/10, 2010 at 14:32 Comment(5)
What's the functional difference between doing this and having myMock.DidStuffWith(...) return the list it was asked to do stuff with, which could be checked locally?Liftoff
@arootbeer Methods which change things and return a value eventually get used for returning the value, and changing things becomes a horrible side effect. Seen it too many times. Either change state or return a value, but not both (except in the case of fluent interfaces, which are designed specifically with the return interface in mind). Functionally there would be no difference. OOally there is.Bulldoze
so myMock.DidStuffWith(...) is a void method? If so, then I completely agree with you.Liftoff
@arootbeer Yes, it is (it's actually a Save on a repository).Bulldoze
gotcha. I'll add an answer with what I believe to be a more convenient way to deal with itLiftoff
R
5

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

Robson answered 21/10, 2010 at 14:33 Comment(5)
This works with an expression but not with a lambda with an expression body. Updated to include that in my example. Thank you for teaching me that bit though; I hadn't tried it without the brackets.Bulldoze
@Kirk Woll: "Any lambda can be converted into an expression tree." This is false. Consider lambdas that have an expression body.Subtract
Anyone able to explain what it is about the nature of expression trees that prevents this? Or is it just that they compile into something horribly (or beautifully) functional and functions can't contain state?Bulldoze
@Lunivore, please see Eric Lippert's answer here: #1431238 : "That is an obvious and useful feature which we simply did not have time to get to for C# 4. We'll consider it for hypothetical future versions. If you have a really great user scenario for statement trees, I'd love to hear it."Robson
@Kirk Woll, thanks. I've just updated Eric's answer since this is obviously a really great user scenario.Bulldoze
L
1

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);
Liftoff answered 22/10, 2010 at 13:46 Comment(3)
If I was trying to Setup a method, sure. However, I'm using Verify. I dislike the whole Given, Expect, When, Then go back and re-read everything because your desired outcome happened somewhere bizarre in the middle, so Setup isn't going to cut it. I prefer the outcome to be at the end of the test. It's more readable that way.Bulldoze
(I used a method instead btw - that solved the problem. And got a thank you from Eric. So thank you too!)Bulldoze
Interesting - I came to Moq from Rhino Mocks (late 2.x or early 3.x IIRC) where you had to set everything up explicitly. I noticed that you don't have to do that with Moq, but I'd never thought about leaving off the Arrange part and just Asserting at the end.Liftoff

© 2022 - 2024 — McMap. All rights reserved.