RajOpteamix / moq

Automatically exported from code.google.com/p/moq
Other
0 stars 0 forks source link

Mocking Method with Delegate as Parameter #300

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?

I have something like the following code: 

public interface IStore
{
   T GetBy<T>(Func<T, bool> criteria);
}

public class UserFinder
{
   private IStore _store;

   public UserFinder(IStore store)
   {
      _store = store;
   }

   public UserPOCO GetBy(string username)
   {
      return _store.GetBy<UserPOCO>(user=> user.Username == username);
   }
}

And wish to do the following mock:

var mockOfStore= new Mock<IStore>();
mockOfStore.Setup(store => store.GetBy<UserPOCO>(user => user.Username == 
"Username")).Returns(new UserPOCO()).Verifiable();
new UserFinder(mockOfStore.Object).GetBy("Username");
mockOfStore.Verify();

What is the expected output? What do you see instead?

I just wanted to know if it is possible to do this, am I doing something wrong 
or would this be a feature request?

Error message returned: 

System.NotSupportedException: Unsupported expression: user=> (user.Username == 
"Username")

What version of the product are you using? On what operating system?
- 4.0.10827.0
- Windows 7

Original issue reported on code.google.com by thechris...@gmail.com on 5 Jan 2011 at 7:15

GoogleCodeExporter commented 9 years ago
The underlying problem is in the core of C# or rather, whole .Net.

You see, for your example to run properly, the MOQ qould have to be able to 
COMPARE LAMBDAS. The lambda you are passing in

    public UserPOCO GetBy(string username)
    {
       return _store.GetBy<UserPOCO>(user=> user.Username == username);
    }

and the lambda you use in Setup:

    mockOfStore.Setup(store => store.GetBy<UserPOCO>(user => user.Username == "Username"))

surely look similar for you, but for C#/.Net, they are SEPARATE, and different. 
They will compiled as to separate anonymous functions, and therefore thir 
object-references will be always inequal. Well, of course, if compiler or JIT 
were intelligent enough, he could merge them into one, but still either of 
those behaviours is not guaranteed to happen.

As we have two functions, how now the MOQ could check if they are the same? The 
IL code could be read and compared, etc, but in fact, there is no way to 
compare them. Even slight disturbance like "user.Username=='name'" versus 
"'name'==user.Username" could render different IL code, and IIRC even two 
identical functions are not guaranteed to have the same IL after compilation.. 
There is no easy way to compare them, or there may even no hard way to do that.

Instead of lambdas, you could use Expressions, which actually are 
tree-comparable. MOQ could take your "user.Username='name'" as an Expression 
instead Lambda, and could use it for comparison. However, it would also need 
your implementation in store.getby  to fetch an expression instead of lambda. 
Only in such setup the MOQ would have a tiny chance to detect equality of those.

This would however place quite a strict requirements on your code, and also 
would make your store.GetBy not only more complex, but also less performant, as 
it would have to compile/cache the Expression into invocable lambdas.

I'm not designer of MOQ, I've just contributed a few patches, but looking at 
this from the "feasibility" point of view, I'd really bet this to be not only a 
feature request, but even a goal impossible to achieve.

Please let me know if I'm wrong! I'd really love to see any way to compare 
delegates, or to make the use of Expressions less burdening!

Original comment by quetzalc...@gmail.com on 9 Dec 2012 at 1:00