简体   繁体   中英

Moq setup treats all empty enumerables/arrays as the same parameter

I have a method that accepts an IEnumerable :

MyMethod(IEnumerable<MyClass> myParameter)

Now I am writing this code to mock the service:

var array1 = new MyClass[0];
var array2 = new MyClass[0];

_service
   .Setup(s => s.MyMethod(array1))
   .Returns(value1);

_service
   .Setup(s => s.MyMethod(array2))
   .Returns(value2);

And finally I am doing two calls to the service with both arrays inside system under test:

_service.MyMethod(array1);
_service.MyMethod(array2);

What I do expect is to get value1 and value2 from these calls, but in practice the latter call overrides the first one and I only get value2 from both calls.

Is this a bug in Moq or is this a feature that setup treats IEnumerable not as a separate object but rather tries to expand it and compare all elements or something (resulting in two empty arrays being the same setup call)?

When you create multiple setups on a method with Moq, each subsequent setup will replace the previous setup unless the setups are conditional (they specify certain conditions on the arguments). See this answer .

You can fix your code by specifying that the arguments must match the ones you intend to pass:

[Test]
public void MyTest()
{
    var service = new Mock<MyClass>();
    var array1 = new MyClass[0];
    var array2 = new MyClass[0];

    var value1 = "value1";
    var value2 = "value2";

    service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array1))).Returns(value1);          
    service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array2))).Returns(value2);

    Assert.AreEqual(value1, service.Object.MyMethod(array1));
    Assert.AreEqual(value2, service.Object.MyMethod(array2));
}

The behaviour you describe is the default behaviour of the moq , you can see it here . It indeed unfold enumerable and invoke IEnumerable.SequenceEqual . However that is default behaviour(if you setup using an instance, Constant matcher) and you could override it. The one approach is what Owen suggested to use It.Is<T> matcher, eg

service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array1)))
service.Setup(s => s.MyMethod(It.Is<IEnumerable<MyClass>>(e => e == array2)))

Notice that == by default do ReferenceEquals() so this will make different non overridable setups.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM