【问题标题】:How to write unitTest for methods using a stream as a parameter如何为使用流作为参数的方法编写 unitTest
【发布时间】:2015-07-24 14:10:31
【问题描述】:

我有 ImportProvider 类,我想为 Import 方法编写单元测试。

但这应该是单元测试,所以我不想从文件读取到流。 有什么想法吗?

public class ImportProvider : IImportProvider
{ 
     public bool Import(Stream stream)
     {
         //Do import

         return isImported;
     }
}

public interface IImportProvider
{
      bool Import(Stream input);
}

这是单元测试:

[TestMethod]
public void ImportProvider_Test()
{
    // Arrange           
    var importRepository = new Mock<IImportRepository>(); 
    var imp = new ImportProvider(importRepository.Object);
    //Do setup...

    // Act
    var test_Stream = ?????????????
    // This working but not option:
    //test_Stream = File.Open("C:/ExcelFile.xls", FileMode.Open, FileAccess.Read);
    var result = imp.Import(test_Stream);

    // Assert    
    Assert.IsTrue(result);
}

【问题讨论】:

  • 你能改变生产代码吗?

标签: c# unit-testing stream


【解决方案1】:

使用 MemoryStream。不确定您的函数期望什么,但例如将 UTF-8 字符串填充到其中:

//Act
using (var test_Stream = new MemoryStream(Encoding.UTF8.GetBytes("whatever")))
{
    var result = imp.Import(test_Stream);

    // Assert    
    Assert.IsTrue(result);
}

编辑:如果您需要 Excel 文件,并且无法从磁盘读取文件,您能否在测试项目中添加 Excel 文件作为嵌入式资源?见How to embed and access resources by using Visual C#

然后您可以像这样读取流:

//Act
using (var test_Stream = this.GetType().Assembly.GetManifestResourceStream("excelFileResource"))
{
    var result = imp.Import(test_Stream);

    // Assert    
    Assert.IsTrue(result);
}

【讨论】:

  • 有什么理由不能像那样加载测试文件?
  • @user2451446,所以您真正的问题是您想将 Excel 文件的内容提供给被测代码,但不想从 Excel 文件中读取该数据。您将不得不在某个地方做出妥协:要么将其作为集成测试,要么移除对流特定内容的依赖。
  • 已添加有关如何将文件添加为资源的信息
  • @GazTheDestroyer,当我声称对糟糕的选择进行二分法并且有人添加了第三种完美的解决方案时,我喜欢它。我从来没有想过资源。这完全解决了OP的需求。可悲的是,我不能第二次 +1 :)
  • 我不同意。如果文件嵌入到测试中,那么它仍然是测试与一个代码单元。您抽象出数据的实际来源并测试只是数据可以被正确处理。有多种方法可以嵌入数据。最明显的是要么生成一个实际文件,然后将其设为“嵌入式资源”,要么针对某种 Excel DOM 提供程序编写代码,该提供程序以预期格式动态生成数据。
【解决方案2】:

您可以使用 MemoryStream 为您的测试提供纯粹的内存流。

【讨论】:

    【解决方案3】:

    我的解决方案是将Stream 包装在您自己的类中,我们将其命名为StreamWrapper,它实现了接口IStream。现在将导入签名更改为

    public bool Import(IStream stream)
    

    现在您可以模拟该流,如果您想在生产代码中使用它,您应该传递 StreamWrapper 否则模拟。

    【讨论】:

    • 如何用任何有意义的数据模拟流?
    • 这无济于事,因为该方法显然需要流中的 Excel 数据(请参阅 cmets 对 GazTheDestroyer 的回答)。
    • @user2451446 这取决于 Import 方法的内容是什么,使用什么,预期什么等 DavidArno 我不明白你的意思是什么?如果您包装整个流类,您将能够使用流提供的所有功能,但对于另一个类,我不明白它会在哪里失败。
    • @Zbigniew,该模拟类必须在请求时提供 Excel 数据。无法从文件中读取该数据……因此 OP 遇到了不可能的情况。模拟类不能解决任何 MemoryStream 也无法解决的问题。
    【解决方案4】:

    使用 JustMock、TypeMock 或 Microsoft Fakes 等隔离框架,您将能够模拟 Stream。

    坏消息是,据我所知,所有这些都是有偿的。

    【讨论】:

    • 我认为这归结为两件事:1)有像 NSubstitute 这样完全免费使用的模拟框架,2)可以“模拟”流,而不必模拟实际的流类型,因为 a) Stream 类型本身可以在任意子类中直接扩展,并且 b) 如果您只需要能够读取或写入数据,则内置的 MemoryStream 类型可以提供 Stream 之间的接口语义和byte[] 缓冲区。所以,这个答案并不能准确地描述问题空间或潜在的解决方案。
    • @JonathanGilbert:您可能认为还有其他选择,并且您可能认为它们比这个更好,但我认为隔离框架是这个问题的潜在解决方案是无可争议的。
    • 好的,是的,从技术上讲,您可以使用模拟框架来模拟 Stream 对象。但是,您可以使用简单的子类更轻松地创建一个模拟 Stream 对象。最重要的是,您在回答中声明 所有 模拟框架都是付费的,这完全是不真实的。因此,您的回答具有误导性,即使前半部分在技术上是正确的,并且您的回答建议做一些“艰难的方式”,而没有表明或承认有更简单的方法。 是其他 Stack Overflow 成员评价它的答案很差的原因。
    • 这是不正确的,微软假货不收费。答案应该删除。
    • @SkorunkaFrantišek:没付钱?它仅适用于 Visual Studio 终极版或高级版。你知道这些多少钱吗?这是最贵的:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-07
    • 1970-01-01
    • 1970-01-01
    • 2020-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多