是否有任何库或方法来模拟C#中的文件系统来编写单元测试?在我目前的情况下,我有方法检查是否存在某个文件并读取创建日期。我将来可能还需要更多。
答案 0 :(得分:138)
编辑:安装NuGet包System.IO.Abstractions
。
最初接受此答案时,此程序包不存在。原始答案是根据以下历史背景提供的:
您可以通过创建界面来实现:
interface IFileSystem { bool FileExists(string fileName); DateTime GetCreationDate(string fileName); }
并创建一个使用的“真实”实现 System.IO.File.Exists()等你可以使用a来模拟这个接口 嘲弄框架;我推荐Moq。
编辑:有人完成了这项工作,并在网上发布here。
我已经用这种方法模拟了IClock中的DateTime.UtcNow 接口(对我们的测试真正有用,能够控制 流动的时间!),更传统的是,一个ISqlDataAccess 接口
另一种方法可能是使用TypeMock,这可以让你 拦截对类的调用并将其存根。然而这确实是成本 钱,需要安装在整个团队的PC上 你的构建服务器为了运行,它显然也无法运行 System.IO.File,因为它can't stub mscorlib。
您也可以接受某些方法不可单元测试 并在单独的慢速运行集成/系统测试中测试它们 套件。
答案 1 :(得分:75)
此虚构库现在存在,System.IO.Abstractions有一个NuGet包,它抽象出System.IO命名空间。
还有一组测试助手,System.IO.Abstractions.TestingHelpers - 在撰写本文时 - 仅部分实现,但是一个非常好的起点。
答案 2 :(得分:9)
您可能需要构建一个合同来定义文件系统中需要的内容,然后围绕这些功能编写包装器。那时你就可以模拟或删除实现了。
示例:
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"); }
}
答案 3 :(得分:5)
我的建议是使用http://systemwrapper.codeplex.com/ 因为它为System名称空间
中主要使用的类型提供了包装器答案 4 :(得分:3)
我遇到了以下解决方案:
我最终使用上面的所有方法,这取决于我写的内容。但是大多数时候,当我编写击中IO的单元测试时,我最终认为抽象是错误的。
答案 5 :(得分:2)
使用 System.IO.Abstractions 和 System.IO.Abstractions.TestingHelpers 像这样:
public class ManageFile {
private readonly IFileSystem _fileSystem;
public ManageFile(IFileSystem fileSystem){
_fileSystem = fileSystem;
}
public bool FileExists(string filePath){}
if(_fileSystem.File.Exists(filePath){
return true;
}
return false;
}
}
在测试类中,您使用MockFileSystem()模拟文件,并实例化ManageFile,如:
var mockFileSysteme = new MockFileSystem();
var mockFileData = new MockFileData("File content");
mockFileSysteme.AddFile(mockFilePath, mockFileData );
var manageFile = new ManageFile(mockFileSysteme);
答案 6 :(得分:1)
我不确定如何模拟文件系统。您可以做的是编写一个测试夹具设置,创建一个具有必要的测试结构的文件夹等。在测试运行后,拆卸方法会将其清理干净。
编辑补充:在考虑这个问题时,我不认为你想要模拟文件系统来测试这种类型的方法。如果你模拟文件系统,如果某个文件存在则返回true,并在测试该文件是否存在的方法中使用它,那么你就不会测试任何东西了。模拟文件系统有用的地方是,如果你想测试一个依赖于文件系统的方法,但文件系统活动不是被测试方法的组成部分。
答案 7 :(得分:1)
在测试中模拟文件系统会很困难,因为.NET文件API实际上并不是基于可以模拟的接口或可扩展类。
但是,如果您有自己的功能层来访问文件系统,则可以在单元测试中模拟它。
作为模拟的替代方法,请考虑在测试设置中创建所需的文件夹和文件,并在拆解方法中删除它们。
答案 8 :(得分:1)
回答您的具体问题:不,没有库可以让您模拟文件I / O调用(我知道)。这意味着“正确”单元测试您的类型将要求您在定义类型时考虑此限制。
关于如何定义“正确”单元测试的快速说明。我相信单元测试应该确认您获得了已知输入的预期输出(是异常,调用方法等)。这允许您将单元测试条件设置为一组输入和/或输入状态。我发现这样做的最好方法是使用基于接口的服务和依赖注入,以便通过构造函数或属性传递的接口提供类型外部的每个职责。
所以,考虑到这一点,回到你的问题。我通过创建IFileSystemService
接口以及FileSystemService
实现来模拟文件系统调用,该实现只是mscorlib文件系统方法的一个外观。然后我的代码使用IFileSystemService
而不是mscorlib类型。这允许我在应用程序运行时插入标准FileSystemService
或在我的单元测试中模拟IFileSystemService
。无论运行方式如何,应用程序代码都是相同的,但底层基础架构允许轻松测试代码。
我将承认在mscorlib文件系统对象周围使用包装器是一件痛苦的事情,但在这些特定情况下,随着测试变得更加容易和可靠,它值得额外工作。
答案 9 :(得分:1)
创建一个界面并模拟它进行测试是最干净的方法。但是,作为替代方案,您可以查看Microsoft Moles框架。
答案 10 :(得分:1)
您可以使用Microsoft Fakes执行此操作,而无需更改代码库,例如因为它已被冻结。
System.dll的第一个generate a fake assembly - 或任何其他包,然后模拟预期返回,如:
using Microsoft.QualityTools.Testing.Fakes;
...
using (ShimsContext.Create())
{
System.IO.Fakes.ShimFile.ExistsString = (p) => true;
System.IO.Fakes.ShimFile.ReadAllTextString = (p) => "your file content";
//Your methods to test
}
答案 11 :(得分:0)
常见的解决方案是使用一些抽象的文件系统API(如Apache Commons VFS for Java):所有应用程序逻辑都使用API,单元测试能够使用存根实现模拟真实的文件系统(内存模拟或类似的东西)。
对于C#,存在类似的API:NI.Vfs,它与Apache VFS V1非常相似。它包含本地文件系统和内存文件系统的默认实现(最后一个可用于框中的单元测试)。
答案 12 :(得分:-1)
我们目前使用专有数据引擎,其API不作为接口公开,因此我们很难对我们的数据访问代码进行单元测试。然后我也和Matt和Joseph的方法一起去了。
答案 13 :(得分:-2)
我会选择Jamie Ide的回复。不要试图嘲笑你没写的东西。会有各种你不了解的依赖关系 - 密封课程,非虚拟方法等。
另一种方法是使用可模拟的东西包装appopiate方法。例如创建一个名为FileWrapper的类,它允许访问File方法,但是你可以模拟出来。