【问题标题】:How do I mock an IEnumerable<T> so that I can test a method that receives it如何模拟 IEnumerable<T> 以便测试接收它的方法
【发布时间】:2010-12-16 06:36:04
【问题描述】:

我想测试一个方法,它需要 IEnumerable&lt;T&gt; 作为参数。

我目前正在模拟IEnumerable&lt;T&gt; 的内容如下(使用起订量):

 var mockParent = new Mock<ICsvTreeGridExportable>();
 var mockChild = new Mock<ICsvTreeGridExportable>();

如何将这些模拟对象放在IEnumerable&lt;T&gt; 中,以便我可以将它们作为参数传递给我要测试的方法?

我正在测试的方法预计会收到IEnumerable&lt;ICsvTreeGridExportable&gt;

【问题讨论】:

    标签: .net unit-testing mocking moq


    【解决方案1】:

    这是塞巴斯蒂安答案的替代方法,可让您指定所需的任何类型的假人数量:

    private IEnumerable<T> GetDummies<T>(int numDummies) where T : new() {
        for (int i = 0; i < numDummies; i++) yield return new T();
        yield break;
    }
    

    或者(如果你想使用能够使用没有空构造函数的类型):

    private IEnumerable<T> GetDummies<T>(Func<T> generator, int numDummies) {
        for (int i = 0; i < numDummies; i++) yield return generator.Invoke();
        yield break;
    }
    

    【讨论】:

      【解决方案2】:

      我会使用集合初始化器语法创建一个数组。即

      var mockParent = new Mock<ICsvTreeGridExportable>();
      var mockChild = new Mock<ICsvTreeGridExportable>();
      
      TestMethod(new[] { mockParent.Object, mockChild.Object });
      

      .NET 中的数组实现了IEnumerable&lt;T&gt; 接口,所以一切就绪。

      注意:如果你想要一个“纯”IEnumerable&lt;T&gt;(正如 Luke 指出的那样),你可以使用一点 LINQ 来做到这一点:

      TestMethod((new[] { mockParent.Object, mockChild.Object }).TakeWhile(true));
      

      【讨论】:

      • yap...比我的解决方案简单得多 (+1)
      • Noldorin,如果您可以将答案更改为具有 mockParent.Object 和 mockChild.Object,那么我可以将其标记为正确答案。下面的卢克斯回答也非常有帮助。很遗憾我不能将两者都标记为已接受:)
      【解决方案3】:

      你可以创建一个数组。 (数组实现了IEnumerable&lt;T&gt; 接口。)

      var mockEnumerable = new[] { mockParent.Object, mockChild.Object };
      

      如果您想要一个“纯”IEnumerable&lt;T&gt; 不能转换回数组等,那么您可以使用辅助方法创建它:

      var mockEnumerable = CreateEnumerable(mockParent.Object, mockChild.Object);
      
      // ...
      
      public static IEnumerable<T> CreateEnumerable<T>(params T[] items)
      {
          foreach (T item in items)
          {
              yield return item;
          }
      }
      

      (正如 Jamie 在 cmets 中提到的,您需要使用 mocked 对象,而不是 Mock 对象。例如,mockParent.ObjectmockChild.Object等等,只是mockParentmockChild。)

      【讨论】:

      • 谢谢卢克。当我尝试将 mockEnumerable 传递给该方法时,我收到以下错误:Argument.Type 'Moq.Mock[] 不可分配给参数类型 IEnumerable。我需要做些什么来确保我的模拟对象是正确的类型?
      • CreateEnumerable 方法正是我最初考虑的方法,但它与数组相比并没有任何优势。无论如何,编译器都会生成一个虚拟类,所以嗯。但是,如果您想要延迟初始化(请参阅塞巴斯蒂安的回答),它可能会很有用。
      • 这仍然给了我同样的问题。当我使用 IEnumerable x = CreateEnumerable(mockParent, mockChild);我仍然被告知“无法转换源类型”。一个是 IEnum... 类型,另一个是 IEnum... 类型。我肯定在这里遗漏了一些非常简单的东西。
      • @Noldorin:使用“纯”枚举对于测试很方便。我见过声称采用IEnumerable&lt;T&gt; 但随后在内部盲目地转换为IList&lt;T&gt; 或类似方法的方法。如果您使用数组或列表测试这些方法,那么它们将通过,使用“纯”IEnumerable&lt;T&gt; 进行测试,您会发现这些错误!
      • 好的,在你们的大力帮助下,我已经弄清楚了。我需要做的是将模拟对象对象添加到数组中以在我的方法中使用。 var mockEnumerable = new[] {m​​ockParent.Object, mockChild.Object}; -- 所以我使用mockParent.Object 而不仅仅是mockParent。这现在转换为我的方法接受的正确类型。 :)
      【解决方案4】:
      List<ICsvTreeGridExportable> myList = new List<ICsvTreeGridExportable>();
      myList.Add(mockParent);
      myList.Add(mockChild);
      return myList;
      

      【讨论】:

        【解决方案5】:

        你可以做这样的事情:
        创建一个函数 Dummy

        private IEnumerable<ICsvTreeGridExportable> Dummy()
        {
             yield return new ICsvTreeGridExportable();
        }
        

        在你的测试函数中做类似

        private void TestFunction()
        {
           ThisIsTheOneThatNeedsIenumerable(Dummy());
        }
        

        希望对你有帮助

        【讨论】:

        • 我认为他想要一个包含多个元素的IEnumerable
        • 是的,有了收益回报,您可以根据需要返回任意数量...我错过了...
        • @sebastian:确实。但是,如果您想要“延迟初始化”,则此方法可能很有用 - 即仅在需要时创建对象。
        • 这个Dummy() 函数不是要提供一个永无止境的对象集吗?
        • nop... 实际上在给定的示例中它只会返回一个对象
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-06
        相关资源
        最近更新 更多