使用 Moq 模拟 GetEnumerator

Mocking GetEnumerator using Moq

我正在尝试模拟 Microsoft.Office.Interop.Word 程序集中的 Variables 接口

var variables = new Mock<Variables>();
variables.Setup(x => x.Count).Returns(2);
variables.Setup(x => x.GetEnumerator()).Returns(TagCollection);

private IEnumerator TagCollection()
{
    var tag1 = new Mock<Variable>();
    tag1.Setup(x => x.Name).Returns("Foo");
    tag1.Setup(x => x.Value).Returns("Bar");

    var tag2 = new Mock<Variable>();
    tag2.Setup(x => x.Name).Returns("Baz");
    tag2.Setup(x => x.Value).Returns("Qux");

    yield return tag1.Object;
    yield return tag2.Object;
}

我的代码如下所示:

// _variables is an instance of Variables interface
var tags = from variable in _variables.OfType<Variable>()
           where variable.Name == "Foo"
           select variable.Value;
var result = tags.ToList();

上面代码的最后一行抛出 NullReferenceException。如果我使用 foreach 循环遍历 _variables 集合,我可以毫无问题地访问 Variable 的模拟对象。我在这里做错了什么?

尝试:

variables
    .As<IEnumerable>()
    .Setup(x => x.GetEnumerator()).Returns(TagCollection);

有两种不同的方法,一种在基接口中声明,一种在Variables中声明。

当你直接 foreach 时,后者会被调用,因为该方法 隐藏了 基础中长相相同的成员类型。 foreach 在存在时调用 public 方法,在这种情况下 IEnumerable 无关紧要。

当您调用 .OfType<Variable>() Linq 扩展时,引用被强制转换为 IEnumerable 接口,名称隐藏不再存在。调用了基础接口上的方法。

就像是两者的区别:

_variables.GetEnumerator();

和:

((IEnumerable)_variables).GetEnumerator();

您可以将生成的模拟起订量想象成这样:

public class TheTypeMoqMakes : Variables 
{
  Enumerator Variables.GetEnumerator()
  {
    // Use return value from after
    // expression tree you provided with 'Setup' without 'As'.
    // If you did not provide one, just return null.
  }

  Enumerator IEnumerable.GetEnumerator()
  {
    // Use return value from after
    // expression tree you provided with 'Setup' with 'As<IEnumerable>'.
    // If you did not provide one, just return null.
  }

  // other methods and properties
}

在成员不是 Setup 的情况下 Moq returns 为空的原因是您有 MockBehavior.Loose。始终考虑 MockBehavior.Strict


我无法理解为什么 Variables 接口 chose to use method hiding 的作者会出现这种情况。