你如何嘲笑C#中的文件系统进行unit testing?

是否有任何图书馆或方法来嘲笑C#中的文件系统编写unit testing? 在我目前的情况下,我有检查某些文件是否存在并读取创builddate的方法。 将来我可能需要更多。

编辑:安装NuGet包System.IO.Abstractions。

当这个答案最初被接受时,这个包不存在。 最初的答案是针对以下历史情况提供的:

你可以通过创build一个接口来实现:

interface IFileSystem { bool FileExists(string fileName); DateTime GetCreationDate(string fileName); } 

并创build一个使用System.IO.File.Exists()等的“真正的”实现。然后你可以使用模拟框架来模拟这个接口。 我推荐Moq 。

编辑:有人完成了这一点,并亲切地在网上发布。

我使用这种方法来模拟IClock接口中的DateTime.UtcNow(对于我们的testing来说,能够控制时间stream程真的非常有用!),更传统上是一个ISqlDataAccess接口。

另一种方法可能是使用TypeMock ,这样可以拦截对类的调用并将其存储。 然而,这样做会花钱,而且需要将其安装在整个团队的PC和构build服务器上才能运行,而且显然不能用于System.IO.File,因为它不能存储mscorlib 。

您也可以接受某些方法不是unit testing,而是在单独的慢速运行集成/系统testing套件中进行testing。

安装包System.IO.Abstractions

这个虚构的库现在存在, System.IO.Abstractions有一个NuGet包,它抽象出System.IO命名空间。

还有一套testing助手,System.IO.Abstractions.TestingHelpers,在撰写本文的时候,它只是部分实现,但是是一个非常好的起点。

您可能将不得不build立一个合约,以便从文件系统中定义您需要的东西,然后为这些function编写封装。 在这一点上,你可以模拟或者删除实现。

例:

 interface IFileWrapper { bool Exists(String filePath); } class FileWrapper: IFileWrapper { bool Exists(String filePath) { return File.Exists(filePath); } } class FileWrapperStub: IFileWrapper { bool Exists(String filePath) { return (filePath == @"C:\myfilerocks.txt"); } } 

我遇到以下解决scheme:

  • 写集成testing,而不是unit testing。 为了这个工作,你需要一个简单的方法来创build一个文件夹,在那里你可以转储的东西,而不用担心其他testing干扰。 我有一个简单的TestFolder类,它可以创build一个独特的每个testing方法文件夹来使用。
  • 写一个可嘲笑的System.IO.File。 这是创build一个IFile.cs 。 我发现使用它往往会以testing结束,简单地certificate您可以编写模拟语句,但是在IO使用率很小时使用它。
  • 检查你的抽象层,并从类中提取文件IO。 为此创build一个接口。 其余部分使用集成testing(但这将是非常小的)。 这与上面的不同之处在于,不是做文件。读你写意图,说ioThingie.loadSettings()
  • System.IO.Abstractions 。 我还没有使用过,但这是我最兴奋的一个。

我最终使用上面的所有方法,这取决于我正在写什么。 但是大多数情况下,当我写unit testing碰到IO的时候,我认为抽象是错误的。

在testing中嘲笑文件系统是困难的,因为.NET文件API并不是真正基于可能被嘲笑的接口或可扩展类。

但是,如果你有自己的function层来访问文件系统,你可以在unit testing中嘲笑它。

作为嘲笑的替代方法,可以考虑在testing设置中创build需要的文件夹和文件,并在拆卸方法中删除它们。

我不知道你将如何模拟文件系统。 你可以做的是写一个testing夹具设置,创build一个文件夹等与testing的必要结构。 拆卸方法将在testing运行后清理。

编辑补充说:在考虑这一点之后,我不认为你想嘲笑文件系统来testing这种types的方法。 如果你嘲笑文件系统返回真如果某个文件存在,并在你的testing方法,检查该文件是否存在,使用它,那么你不testing任何东西。 如果你想testing一个依赖于文件系统的方法,但是文件系统活动并不是被测方法中不可或缺的一部分,那么模拟文件系统将会很有用。

回答你的具体问题:不,没有库允许你模拟文件I / O调用(我知道的)。 这意味着“正确地”unit testing你的types将要求你在定义你的types时考虑到这个限制。

关于如何定义“正确的”unit testing的快速说明。 我相信unit testing应该确认你得到预期的输出(是一个例外,调用一个方法等)提供已知的input。 这使您可以将您的unit testing条件设置为一组input和/或input状态。 我发现这样做的最好方法是使用基于接口的服务和dependency injection,以便通过通过构造函数或属性传递的接口提供types外部的每个责任。

所以,考虑到这一点,回到你的问题。 我通过创build一个IFileSystemService接口和一个FileSystemService实现来嘲笑文件系统调用,而这个实现只是mscorlib文件系统方法的一个外观。 我的代码然后使用IFileSystemService而不是mscorlibtypes。 这允许我在应用程序运行时插入标准FileSystemService ,或者在unit testing中模拟IFileSystemService 。 应用程序代码无论如何运行都是相同的,但底层基础架构允许轻松testing代码。

我会承认,使用mscorlib文件系统对象的包装是一件痛苦的事情,但在这些特定的场景下,值得做更多的工作,因为testing变得更容易,更可靠。

创build一个界面并嘲笑它进行testing是最干净的方法。 但是,作为一个替代哟可以看看微软的鼹鼠框架。

通常的解决scheme是使用一些抽象的文件系统API(如Apache Commons VFS for Java):所有的应用程序逻辑使用API​​和unit testing,能够用存根实现(内存模拟或类似的东西)来模拟真正的文件系统。

对于C#,类似的API存在: NI.Vfs ,它非常类似于Apache VFS V1。 它包含本地文件系统和内存中文件系统的默认实现(最后一个可以在框中的unit testing中使用)。

我会和Jamie Ide一起回应。 不要试图嘲笑你没有写的东西。 将会有你不知道的所有依赖关系 – 密封类,非虚拟方法等

另一种方法是用可嘲弄的东西包装应用程序的方法。 例如创build一个名为FileWrapper的类,允许访问File方法,但是可以将其模拟出来。

我们目前使用专有的数据引擎,其API不作为接口公开,所以我们很难unit testing我们的数据访问代码。 然后,我也跟着马特和约瑟夫的方法去了。