将目标转换为IEnumerable时,Moqing GetEnumerable失败

Sve*_*ann 4 c# ienumerable moq

我正在尝试模拟Visual Studio CommandBars实例.CommandBars实现非通用的IEnumerable接口.为了能够迭代模拟,我设置了GetEnumerable().奇怪的是,这只有在我将mock.Object作为CommandBars的一个实例访问时才有效.如果我转换为IEnumerable(因为它在使用Linq方法时隐式发生),GetEnumerable()突然返回null.有人可以解释这种行为吗?

var mockCommandBars = new Mock<CommandBars>();
IEnumerable bars = new List<CommandBar>();
mockCommandBars.Setup(cb => cb.GetEnumerator()).Returns(bars.GetEnumerator);

var cbs = mockCommandBars.Object;
var cbs1 = cbs.GetEnumerator();  // returns instance
var ecbs = (IEnumerable) cbs;
var cbs2 = ecbs.GetEnumerator(); // returns null!
Run Code Online (Sandbox Code Playgroud)

编辑:我正在使用Moq 4.2.1402.2112

Stu*_*tLC 6

通过检查实际的类型

 var cbs = mockCommandBars.Object;
Run Code Online (Sandbox Code Playgroud)

在运行时,似乎cbs已被包装为:

 cbs    {Castle.Proxies.CommandBarsProxy}
Run Code Online (Sandbox Code Playgroud)

并且演员表会IEnumerable干扰代理人的行为.

您可以在此处使用此帖子中的帮助方法来连接代理的__target属性,例如

var cbs2 = UnwrapProxy<IEnumerator>(cbs.GetEnumerator());
Run Code Online (Sandbox Code Playgroud)

哪里

  internal static TType UnwrapProxy<TType>(TType proxy)
  {
     try
     {
        dynamic dynamicProxy = proxy;
        return dynamicProxy.__target;
     }
     catch (RuntimeBinderException)
     {
        return proxy;
     }
  }
Run Code Online (Sandbox Code Playgroud)

编辑

从这里可以清楚地看到,设置没有在底层_CommandBars.IEnumerable接口上执行

您可以明确更改设置:

     var cbs = mockCommandBars.As<_CommandBars>().As<IEnumerable>();
     cbs.Setup(cb => cb.GetEnumerator()).Returns(bars.GetEnumerator());

     var ecbs = (IEnumerable)cbs.Object; // The cast is now redundant.
     var cbs2 = ecbs.GetEnumerator();
Run Code Online (Sandbox Code Playgroud)

如果你想保留一个模拟变量来传递,你可以像这样设置它.

var mockCommandBars = new Mock<CommandBars>();
mockCommandBars.Setup(cb => cb.GetEnumerator()).Returns(bars.GetEnumerator);
mockCommandBars.As<IEnumerable>().Setup(cb => cb.GetEnumerator()).Returns(bars.GetEnumerator);
Run Code Online (Sandbox Code Playgroud)

这告诉Moq Mock实现了两个接口,并且GetEnumerator两者都是独立定义的.