如何unit testing保存文件到磁盘?

我知道,强烈build议在与文件系统分离的情况下运行unit testing,因为如果在testing中触摸文件系统,还要testing文件系统本身。 好的,这是合理的。
我的问题是,如果我想testing文件保存到磁盘,我该怎么办? 与数据库一样,我分离负责数据库访问的接口,然后为我的testing创build另一个实现? 或者可能还有其他方法?

我对这个方法的看法很大程度上偏向于刚刚阅读的“ 以testing为导向的日益增长的面向对象软件 (GOOS)”的书,但这是我所知道的最好的。 特别:

  • 创build一个接口来从代码中抽象出文件系统。 嘲笑它需要这个类作为协作者/依赖。 这样可以使您的unit testing快速而快速地反馈。
  • 创buildtesting接口的实际实现的集成testing。 即validation调用Save()实际上是否将文件保存到磁盘并具有写入内容(使用参考文件或parsing它应包含的一些内容)
  • 创build一个testing整个系统的验收testing – 端到端。 在这里,您可以validation是否创build了一个文件 – 此testing的目的是确认真正的实现是否正确连接/插入。

更新评论者:

如果您正在阅读结构化数据(例如Book对象)(如果没有将stringreplace为IEnumerable)

interface BookRepository { IEnumerable<Books> LoadFrom(string filePath); void SaveTo(string filePath, IEnumerable<Books> books); } 

现在你可以使用构造函数注入来模拟客户端类。 客户端unit testing因此速度快; 不要打文件系统 。 他们只是validation正确的方法被调用的依赖(例如加载/保存)

 var testSubject = new Client(new Mock<BookRepository>.Object); 

接下来,您需要创buildBookRepository的实际实现,它可以在文件(或者数据库明天如果需要的话)下工作。 没有人必须知道。 为FileBasedBookRepository(实现上述Role)编写集成testing ,并testing使用参考文件调用Load给出正确的对象,并使用已知列表调用Save,将它们保存到磁盘。 即使用真实的文件这些testing会很慢,所以用标签标记或将其移动到一个单独的套件。

 [TestFixture] [Category("Integration - Slow")] public class FileBasedBookRepository { [Test] public void CanLoadBooksFromFileOnDisk() {...} [Test] public void CanWriteBooksToFileOnDisk() {...} } 

最后,应该有一个/更多的验收testing ,执行加载和保存。

你可以不用传递一个文件名到你的保存函数,传递一个Stream,TextWriter或类似的东西。 然后,在testing时,您可以传递基于内存的实现,并validation写入正确的字节,而不实际将任何内容写入磁盘。

为了testing问题和exception,你可以看一下模拟框架。 这可以帮助您在保存过程中的某个时刻人为地生成特定的exception,并testing您的代码是否正确处理它。

一般来说,写文件I / O的unit testing是谨慎的,因为它们往往太慢了。 但是在unit testing中没有绝对禁止文件I / O。

在你的unit testing中有一个临时目录的设置和拆除,并在该临时目录中创buildtesting文件和目录。 是的,你的testing将比纯CPUtesting慢,但是它们仍然会很快。 JUnit甚至有支持代码来帮助这种情况:在@Rule上的@Rule

也就是说,大多数文件编写代码采取这种forms:

  1. 打开输出stream到文件。 此代码必须处理丢失的文件或文件权限问题。 你会想testing它打开文件并应付这些失败情况。
  2. 写入输出stream。 这必须以正确的格式写入,这是需要最多testing的最复杂的部分。 在写入输出stream时,它必须处理I / O错误,尽pipe这些错误在实践中很less见。
  3. closures输出stream。 这在closuresstream时必须处理I / O错误,尽pipe这些错误在实践中是罕见的。

只有第一个处理文件系统。 其余的只是使用输出stream。

所以你可以提取中间和最后一部分到自己的方法(函数),它操纵给定的输出stream,而不是一个命名文件。 然后,您可以模拟该输出stream来unit testing该方法。 那些unit testing会非常快。 大多数编程语言已经提供了合适的输出stream类。

这只剩下第一部分被unit testing。 你将只需要几个testing,所以你的testing套件应该仍然快得可以接受。