2010-02-11 31 views
6

我有一个输出简单报告文件的类。它从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  
     ... 
    } 
    } 

它会更好使用某种自定义的工厂或东西和流传入构造?但是如果涉及到一些商业逻辑,如何呢?文件名是根据查询结果确定的?

或者是整个文件访问的东西不是问题?

道歉,如果我失去了明显的东西。我会很感激任何建议。

+0

我需要问:你的XML是做什么的?它是否只包含指向数据库记录的密钥列表? – 2010-02-11 16:46:02

+0

除了数据库密钥外,它还包含我需要的三个与记录相关的其他字段。这些也被用在报告中。 – James 2010-02-11 16:52:55

+0

相关:http://stackoverflow.com/questions/129036/unit-testing-code-with-a-file-system-dependency – 2010-02-11 16:58:44

回答

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! 
    } 
} 
+0

感谢您的答复。但是我对于如何验证CSV报告文件流以确保我已经写出正确的数据感到困惑。 如果输出流是使用语句的一部分,那么我无法测试它。我应该在课堂级别上引用它,并避免在ReportBiz实例本身处理之前进行处理? – James 2010-02-11 17:09:26

+0

我还没有使用Rhino Mocks,但是在NMock中,我会注入一个模拟的'IStreamFactory',将调用存根到它的'CreateStream'方法,并使其返回一个模拟流,我将设置期望值'Stream.Write')来验证我写了正确的数据。 – 2010-02-11 19:31:45

2

你必须以某种方式嘲笑你的流,以避免它们暴露于测试,因为你的业务逻辑的肉越来越输入字符串和输出st戒指正确。

在这个场景中的硬道理是你必须承认,你在你的数据流三个阶段:

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

老实说,整个文件写入问题并不是什么大问题--.NET Framework提供了经过很好测试的文件写入功能,它超出了您的测试范围。您遇到的大多数问题都是您吐出的CSV文件的准确性。

作为一项预防措施,我建议不要使用自己的CSV编辑器。您应该尝试查找已存在的CSV库 - there are a lot of them over the net