For this question consider (create) the interface:
public interface ITestMe
{
string TakeInt64(long x);
}
Then run the following code:
public void Test()
{
var mock1 = new Mock<ITestMe>(MockBehavior.Strict);
Expression<Func<ITestMe, string>> expr1 = x => x.TakeInt64(2);
Console.WriteLine(expr1);
mock1.Setup(expr1).Returns("OK");
var s1 = mock1.Object.TakeInt64(2L); // OK
var mock2 = new Mock<ITestMe>(MockBehavior.Strict);
Expression<Func<ITestMe, string>> expr2 = x => x.TakeInt64(DateTime.Today.Year / 1000);
Console.WriteLine(expr2);
mock2.Setup(expr2).Returns("OK");
var s2 = mock2.Object.TakeInt64(2L); // OK
var mock3 = new Mock<ITestMe>(MockBehavior.Strict);
Expression<Func<ITestMe, string>> expr3 = x => x.TakeInt64((int)(DayOfWeek)Enum.Parse(typeof(DayOfWeek), "Tuesday"));
Console.WriteLine(expr3);
mock3.Setup(expr3).Returns("OK");
var s3 = mock3.Object.TakeInt64(2L); // OK
var mock4 = new Mock<ITestMe>(MockBehavior.Strict);
Expression<Func<ITestMe, string>> expr4 = x => x.TakeInt64(GetInt32());
Console.WriteLine(expr4);
mock4.Setup(expr4).Returns("OK");
//var s4 = mock4.Object.TakeInt64(2L); // MockException, All invocations on the mock must have a corresponding setup.
var mock5 = new Mock<ITestMe>(MockBehavior.Strict);
Expression<Func<ITestMe, string>> expr5 = x => x.TakeInt64(int.Parse("2"));
Console.WriteLine(expr5);
mock5.Setup(expr5).Returns("OK");
//var s5 = mock5.Object.TakeInt64(2L); // MockException, All invocations on the mock must have a corresponding setup.
var mock6 = new Mock<ITestMe>(MockBehavior.Strict);
Expression<Func<ITestMe, string>> expr6 = x => x.TakeInt64(GetInt32() + 0);
Console.WriteLine(expr6);
mock6.Setup(expr6).Returns("OK");
var s6 = mock6.Object.TakeInt64(2L); // OK
var mock7 = new Mock<ITestMe>(MockBehavior.Strict);
Expression<Func<ITestMe, string>> expr7 = x => x.TakeInt64(1 * int.Parse("2"));
Console.WriteLine(expr7);
mock7.Setup(expr7).Returns("OK");
var s7 = mock7.Object.TakeInt64(2L); // OK
}
static int GetInt32()
{
return 2;
}
In all seven cases we make an expression tree where the TakeInt64
gets an int
(Int32
) instead of a long
(Int64
). However, as is well known, there exists an implicit conversion from int
to long
which will be present in the expression tree (except in expr1
where the compiler converts the constant for us).
How come the cases s4
and s5
above won't work? Curiously, as you can see, if we add 0
or multuply by 1
as in cases s6
and s7
, that works (even if the type is still int
, implicitly converted to long
)?
Please answer before year 3000 because of case expr2
.
I think this is a bug in Moq. The relevant code is in MatcherFactory
. Specifically, the Convert
is removed from the expression so that it can be inspected further. When the remaining topmost expression node is a method call, this node is evaluated lazily. When the remaining expression is not a method call, the whole expression (including Convert
!) is evaluated eagerly.
This means that with lazy evaluation, the comparison is done without the Convert
and object.Equals(2, 2L)
is false
. But with eager evaluation, the Convert
is taken into account and so your code works.
I have made an attempt at fixing this, which seems to fix the issue for me.
(Whew, I almost thought I wouldn't make it in time.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With