Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq - How to call the same setup with different parameters in a loop

Tags:

c#

testing

moq

I'm having some issues setting up one of my mocks in a test that will ultimately call the setup many times but with different parameters so:

var testMock = new Mock<SomeClass>(MockBehavior.Strict);

for (int i = 30000; i <= 300000; i+=10000)
{
   testMock.Setup(x => x.MethodA(SomeStaticClass.GetIt(varA, varB, i), It.IsAny<int>()))
       .Returns(new List<SomeClass>());
}

So, the above doesn't work. It seems like only the last iteration will be "remembered" by the mock. How can I set up many setups on one mock like I intended to do above?

like image 910
Force444 Avatar asked Jan 29 '23 16:01

Force444


1 Answers

When you use external variable from inside lambda expression - it becomes "captured" by this lambda expression and its lifetime is extended. In this example lambda expression is what you pass to Setup call, and external variable is i loop variable. It's lifetime should be extended outside of for loop, because you have no idea what Setup is going to do with that variable - it might use it for a long time after for loop and even encosing function ends. So compiler replaces this local variable with a field in compiler-generated class. Now, all lambdas you passed to Setup in your loop reference exactly the same location - field of compiler-generated class with which i variable was replaced.

When you call mocked function - Moq will compare argument you passed with available setups. But since all setups reference to the same location - they all reference to the last value of i, which it had at the end of the loop.

When you copy loop variable to another variable:

for (int i = 30000; i <= 300000; i+=10000)
{
   var tmp = i;
   testMock.Setup(x => x.MethodA(SomeStaticClass.GetIt(varA, varB, tmp), It.IsAny<int>()))
       .Returns(new List<SomeClass>());
}

It also gets captured and its lifetime is extended, but this time each loop iteration has it's own variable, so it's own instance of compiler-generated class, and now all Setup lambdas use different value.

like image 115
Evk Avatar answered Feb 06 '23 10:02

Evk