Moq setup will not work to invoke a method followed by an implicit conversion

For this question, consider (create) an 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 create an expression tree where TakeInt64 gets int ( Int32 ) instead of long ( Int64 ). However, as you know, there is an implicit conversion from int to long that will be present in the expression tree (except for expr1 , where the compiler will convert the constant for us).

What do the s4 and s5 cases look above? Curiously, if you add 0 or multuply to 1 , as in the cases of s6 and s7 , this works (even if the type is still int , implicitly converted to long ))

Please respond before 3000 due to expr2 case.

+4
source share
1 answer

I think this is a bug in Moq. The corresponding code is in MatcherFactory . In particular, Convert is removed from the expression so that it can be further verified. When the remaining top node expression is a method call, that node evaluates lazily. When the remaining expression is not a method call, the entire expression (including Convert !) Is evaluated impatiently.

This means that with a lazy evaluation, the comparison is done without Convert and object.Equals(2, 2L) false . But with an impatient assessment, Convert taken into account, so your code works.

I made an attempt to fix this , which seems to have fixed this problem for me.

(Wow, I almost thought I wouldn't have time.)

+1
source

Source: https://habr.com/ru/post/1502851/


All Articles