Moq 设置将所有空的 enumerables/arrays 视为相同的参数

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

我有一个接受 IEnumerable:

的方法
MyMethod(IEnumerable<MyClass> myParameter)

现在我正在编写这段代码来模拟服务:

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);

最后,我在被测系统中使用两个数组对服务进行了两次调用:

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

我期望的是从这些调用中得到 value1value2,但实际上后一个调用会覆盖第一个调用,我只能从两个调用中得到 value2

这是 Moq 中的错误还是安装程序将 IEnumerable 视为一个单独的对象而不是尝试扩展它并比较所有元素或其他东西(导致两个空数组相同)的功能设置调用)?

当您使用 Moq 在一个方法上创建多个设置时,每个后续设置都将替换之前的设置,除非设置是有条件的(它们在参数上指定了特定条件)。参见

您可以通过指定参数必须与您打算传递的参数相匹配来修复您的代码:

[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));
}

您描述的行为是moq的默认行为,您可以看到它here。它确实展开可枚举并调用 IEnumerable.SequenceEqual。但是,这是默认行为(如果您使用实例 Constant 匹配器进行设置),您可以覆盖它。一种方法是 Owen 建议使用 It.Is<T> 匹配器,例如

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

请注意,== 默认执行 ReferenceEquals(),因此这将产生不同的不可覆盖设置。