是否有任何库或方法可以在C#中模拟文件系统以编写单元测试?在我目前的情况下,我有一些方法来检查特定文件是否存在并读取其创建日期。将来我可能需要更多的功能。
是否有任何库或方法可以在C#中模拟文件系统以编写单元测试?在我目前的情况下,我有一些方法来检查特定文件是否存在并读取其创建日期。将来我可能需要更多的功能。
编辑:安装NuGet包System.IO.Abstractions
。
该答案被采纳时,此包并不存在。下面为了历史背景提供原始答案:
You could do it by creating an interface:
interface IFileSystem { bool FileExists(string fileName); DateTime GetCreationDate(string fileName); }
and creating a 'real' implementation which uses System.IO.File.Exists() etc. You can then mock this interface using a mocking framework; I recommend Moq.
Edit: somebody's done this and kindly posted it online here.
I've used this approach to mock out DateTime.UtcNow in an IClock interface (really really useful for our testing to be able to control the flow of time!), and more traditionally, an ISqlDataAccess interface.
Another approach might be to use TypeMock, this allows you to intercept calls to classes and stub them out. This does however cost money, and would need to be installed on your whole team's PCs and your build server in order to run, also, it apparently won't work for the System.IO.File, as it can't stub mscorlib.
You could also just accept that certain methods are not unit testable and test them in a separate slow-running integration/system tests suite.
这个虚构的库现在存在了,在NuGet中有一个System.IO.Abstractions的包,它抽象了System.IO命名空间。
还有一组测试助手,System.IO.Abstractions.TestingHelpers,目前只部分实现,但是是一个非常好的起点。
TestableIO.System.IO.Abstractions
和 TestableIO.System.IO.Abstractions.Wrappers
安装到您的主项目中。并将 TestableIO.System.IO.Abstractions.TestingHelpers
安装到测试项目中!
需要 ...Wrappers
来获取 new System.IO.Abstractions.FileSystem()
的 IFileSystem 默认实现。 - huha你可能需要构建一个合同来定义你从文件系统中需要的内容,然后在这些功能周围编写包装器。这样一来,你就可以模拟或者存根实现。
例子:
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"); }
}
通过使用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;
}
}
var mockFileSysteme = new MockFileSystem();
var mockFileData = new MockFileData("File content");
mockFileSysteme.AddFile(mockFilePath, mockFileData );
var manageFile = new ManageFile(mockFileSysteme);
首先生成一个 System.dll 的伪装程序包,或者任何其他软件包,然后进行预期返回值的模拟,如下所示:
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
}
我最终会根据所编写的内容使用以上所有方法。但大多数时候,当我编写单元测试时会涉及IO时,我最终会认为抽象化是错误的。
我不确定您如何模拟文件系统。您可以编写一个测试夹具设置,创建包含测试所需结构的文件夹等。在测试运行后,清理方法将清理它。
编辑以添加:再考虑一下,我认为您不想模拟文件系统来测试此类方法。如果您模拟文件系统以返回某个文件存在并在检查该文件是否存在的方法的测试中使用它,则无法测试任何内容。模拟文件系统有用的地方是,如果您想测试具有对文件系统的依赖项但文件系统活动不是受测试方法的实质组成部分的方法。
在测试中模拟文件系统可能会很困难,因为.NET文件API并不是基于接口或可扩展类的,无法进行模拟。
但是,如果您有自己的功能层来访问文件系统,则可以在单元测试中模拟它。
作为模拟的替代方案,考虑在测试设置的一部分中创建所需的文件夹和文件,并在拆卸方法中删除它们。
IFileSystemService
接口和一个 FileSystemService
实现来模拟文件系统调用,它只是 mscorlib 文件系统方法的外观。然后,我的代码使用 IFileSystemService
而不是 mscorlib 类型。这使我能够在应用程序运行时插入我的标准 FileSystemService
或在单元测试中模拟 IFileSystemService
。应用程序代码无论如何运行都是相同的,但底层基础设施允许该代码易于测试。