单元测试使用文件系统的类

时间:2010-02-11 16:30:38

标签: c# .net unit-testing stream class-design

我有一个输出简单报告文件的类。它从XML文件中读取一些记录ID号:每个用于查找存储在数据库中的匹配记录。然后它会将每个记录的详细信息写入CSV文件。

我想知道 - 组织它的最佳方法是什么,以便它易于测试,但遵循封装原则?我认为除非绝对必要,否则最好避免与文件系统进行交互,以便我处理Stream对象。单元测试时,我可以使用部分模拟对象来覆盖读取或写入文件的位。

我也不确定何时/何处处理流而不使单元测试变得棘手。看起来我可能不得不将流暴露给单元测试。

我的项目使用NHibernate进行数据访问,使用Spring .NET进行依赖注入,使用Rhino.Mocks进行单元测试。

目前我有类似的东西:

public class ReportBiz
{
    //Access to repository, populated by Spring
    internal ICardRequestDAO CardRequestData { get;set;} 

    //This normally returns a FileStream containing data from the XML file. When testing this is overridden by using a Rhino.Mocks partial mock and returns a MemoryStream
    internal virtual Stream GetSourceStream()
    {
        //Load file and return a Stream
        ...
    }

    //This normally returns a FileStream where CSV data is saved. When testing this is overridden by using a Rhino.Mocks partial mock and returns a MemoryStream
    internal virtual Stream GetOutputStream()
    {
        //Create file where CSV data gets saved and return a Stream
        ...
    }

    public void CreateReportFile()
    {
        Stream sourceStream = GetSourceStream();
        ...

        //Create an XmlDocument instance using the stream
        //For each XML element, get the corresponding record from the database
        //Add record data to CSV stream     
        ...
    }
    }

使用某种自定义工厂或其他东西并将流传递给构造函数会更好吗?但是,如果涉及一些业务逻辑,例如,文件名是根据查询结果确定的?

或者毕竟整个文件访问不是问题吗?

如果我遗漏了一些明显的东西,请道歉。我很感激任何建议。

2 个答案:

答案 0 :(得分:8)

最简单的方法是使文件访问模拟,同时保持对可支配资源生命周期的控制,即在您的类中注入StreamFactory

public class ReportBiz {

    private IStreamFactory streamFactory;

    public ReportBiz(IStreamFactory streamFactory) {
        this.streamFactory = streamFactory
    }

    public void CreateReportFile() {
        using(Stream stream = this.streamFactory.CreateStream()) {
            // perform the important work!
        }
    }
}

当涉及更多业务逻辑时,您的工厂方法可能会更精细,但不是很多:

public void CreateReportFile() {
    string sourcePath   = this.otherComponent.GetReportPath();
    using(Stream stream = this.streamFactory.CreateStream(sourcePath)) {
        // perform the important work!
    }
}

答案 1 :(得分:2)

你必须以某种方式模拟你的流,以避免将它们暴露给测试,因为业务逻辑的核心是正确获取输入字符串和输出字符串。

在这种情况下,您必须认识到数据流中有三个阶段:

  • 阅读数据:解析数据是一个独特的,独特的问题
  • 输出的内容:您必须验证给定的正确数据,您有正确的CSV字符串输出
  • 将该内容写入文件

老实说,整个文件编写问题并不是一个大问题 - .NET Framework提供了经过良好测试的文件编写功能,而且超出了测试范围。您将遇到的大多数问题都是您向文件吐出的CSV的准确性。

作为预防性说明,我建议不要滚动自己的CSV编写器。您应该尝试查找已存在的CSV库 - there are a lot of them over the net