我知道强烈建议在与文件系统分离时运行单元测试 ,因为如果你在测试中触摸文件系统,你也可以测试文件系统本身。好的,这是合理的 我的问题是,如果我想测试文件保存到磁盘,我该怎么办?与数据库一样,我将一个负责数据库访问的接口分开,然后为我的测试创建另一个实现?或者可能还有其他方式吗?
答案 0 :(得分:40)
我对此的态度严重偏向我刚刚阅读的Growing Object-Oriented Software Guided by Tests(GOOS)书,但这是我今天所知道的最好的。具体做法是:
评论者更新:
如果您正在阅读结构化数据(例如Book对象)(如果没有替换IEnumerable的字符串)
interface BookRepository
{
IEnumerable<Books> LoadFrom(string filePath);
void SaveTo(string filePath, IEnumerable<Books> books);
}
现在,您可以使用构造函数注入将mock注入客户端类。因此客户类单元测试很快; 不要点击文件系统。他们只是验证在依赖项上调用了正确的方法(例如加载/保存)
var testSubject = new Client(new Mock<BookRepository>.Object);
接下来,您需要创建BookRepository的实际实现,该实现可以在File(或明天的Sql DB,如果您需要)的情况下运行。没有人知道。 为FileBasedBookRepository(实现上述角色)编写集成测试,并测试使用引用文件调用Load给出正确的对象并使用已知列表调用Save,将它们保存到磁盘。即使用真实文件这些测试会很慢,所以用标签标记它们或将它移到一个单独的套件中。
[TestFixture]
[Category("Integration - Slow")]
public class FileBasedBookRepository
{
[Test]
public void CanLoadBooksFromFileOnDisk() {...}
[Test]
public void CanWriteBooksToFileOnDisk() {...}
}
最后应该有一个/多个验收测试,可以进行加载和保存。
答案 1 :(得分:6)
您可以代替将文件名传递给保存功能,传递Stream,TextWriter或类似功能。然后,在测试时,您可以传递基于内存的实现,并验证是否写入了正确的字节,而不实际将任何内容写入磁盘。
要测试问题和异常,您可以查看模拟框架。这可以帮助您在保存过程中的特定点人为地生成特定异常,并测试您的代码是否正确处理它。
答案 2 :(得分:3)
一般规则是谨慎编写执行文件I / O的单元测试,因为它们往往太慢。但是在单元测试中没有绝对禁止文件I / O.
在您的单元测试中,设置并拆除临时目录,并在该临时目录中创建测试文件和目录。是的,您的测试将比纯CPU测试慢,但它们仍然会很快。 JUnit甚至还有支持代码来帮助解决这个问题:@Rule
上的TemporaryFolder
。
也就是说,大多数文件编写代码采用以下形式:
只有第一个真正处理文件系统。其余的只使用输出流。
因此,您可以将中间和最后一部分提取到自己的方法(函数),该方法操作给定的输出流,而不是命名文件。然后,您可以模拟该输出流以对该方法进行单元测试。那些单元测试将非常快。大多数编程语言已经提供了合适的输出流类。
只留下第一部分进行单元测试。您只需要进行一些测试,因此您的测试套件应该仍然可以接受。