简体   繁体   English

您如何在C#中模拟文件系统以进行单元测试?

[英]How do you mock out the file system in C# for unit testing?

Are there any libraries or methods to mock out the file system in C# to write unit tests? 是否有任何库或方法可以模拟C#中的文件系统以编写单元测试? In my current case I have methods that check whether certain file exists and read the creation date. 在我目前的情况下,我有一些方法来检查某些文件是否存在并读取创建日期。 I may need more than that in future. 将来我可能会需要更多。

Edit: Install the NuGet package System.IO.Abstractions . 编辑:安装NuGet包System.IO.Abstractions

This package did not exist when this answer was originally accepted. 最初接受此答案时,此软件包不存在。 The original answer is provided for historical context below: 以下是针对历史背景提供的原始答案:

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; 然后创建一个使用System.IO.File.Exists()等的“真实”实现。 I recommend Moq . 我推荐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. 我已经使用这种方法在IClock接口中模拟出DateTime.UtcNow(对于我们的测试来说确实非常有用,它能够控制时间流!),更传统地,是ISqlDataAccess接口。

Another approach might be to use TypeMock , this allows you to intercept calls to classes and stub them out. 另一种方法是使用TypeMock ,它允许您拦截对类的调用并将其存根。 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 . 但是,这确实要花钱,并且需要在整个团队的PC和构建服务器上安装才能运行,而且,它显然不能用于System.IO.File,因为它不能对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. 您还可以接受某些方法不可单元测试,并在单独的运行缓慢的集成/系统测试套件中对其进行测试。

安装包System.IO.Abstractions

This imaginary library exists now, there is a NuGet package for System.IO.Abstractions , which abstracts away the System.IO namespace. 这个虚构的库现已存在,其中有一个用于System.IO.Abstractions的NuGet程序包,该程序包抽象了System.IO命名空间。

There is also a set of test helpers, System.IO.Abstractions.TestingHelpers which - at the time of writing - is only partially implemented, but is a very good starting point. 还有一组测试帮助程序System.IO.Abstractions.TestingHelpers-在编写本文时-仅部分实现,但这是一个很好的起点。

You're probably going to have to build a contract to define what things you need from the file system and then write a wrapper around those functionalities. 您可能必须建立一个合同来定义文件系统需要什么,然后围绕这些功能编写包装器。 At that point you'd be able to mock or stub out the implementation. 到那时,您将可以模拟或存根实现。

Example: 例:

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"); }
}

我的建议是使用http://systemwrapper.codeplex.com/,因为它为System名称空间中最常用的类型提供了包装器

I've come across the following solutions to this: 我遇到了以下解决方案:

  • Write Integration tests, not unit tests. 编写集成测试,而不是单元测试。 For this to work you need a simple way of creating a folder where you can dump stuff without worrying about other tests interfering. 为此,您需要一种简单的方法来创建一个文件夹,您可以在其中转储内容而不必担心其他测试的干扰。 I have a simple TestFolder class which can create a unique per test method folder to use. 我有一个简单的TestFolder类,可以为每个测试方法文件夹创建一个唯一的文件夹。
  • Write a mockable System.IO.File. 编写一个可模拟的System.IO.File。 That is create a IFile.cs . 那就是创建一个IFile.cs I find using this often ends up with tests that simply prove you can write mocking statements, but do use it when the IO usage is small. 我发现使用它的结果通常是测试,这些测试仅证明您可以编写模拟语句,但是在IO使用量较小时才使用它。
  • Examine you layer of abstraction, and extract the file IO from the class. 检查您的抽象层,并从类中提取文件IO。 The create a interface for this. 为此创建一个接口。 The remainder use integration tests (but this will be very small). 其余的使用集成测试(但这将很小)。 This differs from above in that instead of doing file.Read you write the intent, say ioThingie.loadSettings() 这与上面的不同之处在于它不是执行文件操作。您要写意图,例如ioThingie.loadSettings()
  • System.IO.Abstractions . System.IO.Abstractions I've not used this yet, but it is the one I'm most excited about playing with. 我还没有使用过,但是这是我最兴奋的玩法。

I end up using all the methods above, depending on what I'm writing. 我最终使用上面的所有方法,具体取决于我在写什么。 But most of the time I end up thinking abstraction is wrong when I write unit tests that hit the IO. 但是大多数时候,当我编写影响IO的单元测试时,我总是以为抽象是错误的。

I'm not sure how you would mock up the file system. 我不确定您将如何模拟文件系统。 What you could do is write a test fixture setup that creates a folder, etc. with the necessary structure for the tests. 您可以做的是编写一个测试夹具设置,该创建一个具有测试所需结构的文件夹等。 A teardown method would clean it up after the tests run. 测试运行后,拆解方法将清除它。

Edited to add: In thinking about this a little more, I don't think you want to mock the file system to test this type of methods. 编辑添加:在多考虑这一点时,我认为您不想模拟文件系统来测试这种类型的方法。 If you mock the file system to return true if a certain file exists and use that in your test of a method that checks if that file exists, then you're not testing much of anything. 如果您模拟文件系统是否存在某个特定文件并返回true,并在测试该文件是否存在的方法的测试中使用该文件系统,则您将不需要进行任何测试。 Where mocking the file system would be useful is if you wanted to test a method that had a dependency on the file system but the file system activity was not integral to the method under test. 如果您想测试依赖于文件系统的方法,但是文件系统活动不是被测试方法所不可或缺的,那么模拟文件系统将很有用。

It would be difficult to mock the file system in a test since the .NET file APIs are not really based on interfaces or extensible classes that could be mocked. 在测试中很难模拟文件系统,因为.NET文件API并不是真正基于可以模拟的接口或可扩展类。

However, if you have your own functional layer to access the file system, you could mock that in a unit test. 但是,如果您拥有自己的功能层来访问文件系统,则可以在单元测试中对其进行模拟。

As an alternative to mocking, consider just creating the folders and files you need as part of your test setup, and deleting them in your teardown method. 作为模拟的替代方法,请考虑仅在测试设置中创建所需的文件夹和文件,然后在拆卸方法中将其删除。

To answer your specific question: No, there are no libraries that will allow you to mock file I/O calls (that I know of). 要回答您的特定问题:不,没有库可让您模拟文件I / O调用(据我所知)。 This means that "properly" unit testing your types will require that you take this restriction into consideration when you define your types. 这意味着“正确地”对类型进行单元测试将要求您在定义类型时考虑此限制。

Quick side note about how I define a "proper" unit test. 关于如何定义“适当的”单元测试的简要说明。 I believe that unit tests should confirm that you get the expected output (be that an exception, call on a method, etc) provided known inputs. 我认为,单元测试应确认您提供了已知的输入,从而获得了预期的输出(例如,异常,方法调用等)。 This allows you to set up your unit test conditions as a set of inputs and/or input states. 这使您可以将单元测试条件设置为一组输入和/或输入状态。 The best way I've found to do this is using interface-based services and dependency injection so that each responsibility external to a type is provided via an interface passed via a constructor or property. 我发现执行此操作的最佳方法是使用基于接口的服务和依赖项注入,以便通过构造函数或属性传递的接口来提供类型外部的每个职责。

So, with this in mind, back to your question. 因此,考虑到这一点,请回到您的问题。 I've mocked file system calls by creating a IFileSystemService interface along with a FileSystemService implementation that is simply a facade over the mscorlib file system methods. 我通过创建IFileSystemService接口以及FileSystemService实现(它只是mscorlib文件系统方法的基础)来模拟文件系统调用。 My code then uses the IFileSystemService rather than the mscorlib types. 然后,我的代码使用IFileSystemService而不是mscorlib类型。 This allows me to plug in my standard FileSystemService when the application is running or mock the IFileSystemService in my unit tests. 这使我可以在应用程序运行时插入标准FileSystemService或在单元测试中模拟IFileSystemService The application code is same regardless of how it's run, but the underlying infrastructure allows that code to be easily tested. 无论应用程序代码如何运行,该应用程序代码都是相同的,但是底层基础结构允许对该代码进行轻松测试。

I'll acknowledge that it's a pain to use the wrapper around the mscorlib file system objects but, in these specific scenarios, it's worth the extra work as the testing becomes so much easier and more reliable. 我会承认,在mscorlib文件系统对象周围使用包装器是很痛苦的,但是,在这些特定情况下,由于测试变得更加容易和可靠,因此值得进行额外的工作。

Creating an interface and mocking it for testing is the cleanest way to go. 创建接口并对其进行模拟以进行测试是最干净的方法。 However, as an alternative yo could take a look at the Microsoft Moles framework. 但是,作为替代方案,您可以看看Microsoft Moles框架。

You can do that using Microsoft Fakes without the need to change your codebase for example because it was frozen already. 您可以使用Microsoft Fakes进行此操作,而无需更改代码库,例如,因为它已被冻结。

First generate a fake assembly for System.dll - or any other package and then mock expected returns as in: 首先为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
}

By using System.IO.Abstractions and System.IO.Abstractions.TestingHelpers like that: 通过像这样使用System.IO.AbstractionsSystem.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;
   }
}

In your Test Class, you use MockFileSystem() to mock file and you instanciate ManageFile like: 在测试类中,使用MockFileSystem()模拟文件,然后实例化ManageFile,如下所示:

var mockFileSysteme = new MockFileSystem();
var mockFileData = new MockFileData("File content");
mockFileSysteme.AddFile(mockFilePath, mockFileData );
var manageFile = new ManageFile(mockFileSysteme);

Common solution is using some abstract filesystem API (like Apache Commons VFS for Java ): all application logic uses API and unit test is able to mock real filesystem with stub implementation (in-memory emulation or something like that). 常见的解决方案是使用一些抽象的文件系统API(例如Apache Commons VFS for Java):所有应用程序逻辑都使用API​​,并且单元测试能够通过存根实现(内存中仿真或类似的东西)模拟真实的文件系统。

For C# the similar API exists: NI.Vfs which is very similar to Apache VFS V1. 对于C#,存在类似的API: NI.Vfs ,与Apache VFS V1非常相似。 It contains default implementations both for local filesystem and in-memory filesystem (last one can be used in unit tests from the box). 它包含本地文件系统和内存文件系统的默认实现(可以在框内的单元测试中使用最后一个)。

We currently use a proprietary data engine and its API is not exposed as interfaces so we can hardly unit test our data access code. 当前,我们使用专有的数据引擎,并且其API并未公开为接口,因此我们几乎无法对数据访问代码进行单元测试。 Then I went with Matt and Joseph's approach too. 然后我也接受了马特和约瑟夫的方法。

I would go with Jamie Ide's response. 我同意杰米·艾德的回答。 Don't try to mock out things that you didn't write. 不要试图嘲笑您没有写的东西。 There will be all manner of dependancies you didn't know about - sealed classes, non virtual methods etc. 您将不知道有各种各样的依赖关系-密封类,非虚拟方法等。

Another approach would be to wrap the appopiate methods with something that is mockable. 另一种方法是用一些可模拟的东西来包装appopiate方法。 eg create a class called FileWrapper that allows access to the File methods but is something you can mock out. 例如,创建一个名为FileWrapper的类,该类允许访问File方法,但是您可以模拟一下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 c# 中模拟用于单元测试的记录器? - How to mock a logger for unit testing in c#? 在 C# 中,是否可以模拟 IMessageReceiver 和相关类进行单元测试? - In C#, is it possible to mock out IMessageReceiver and related classes for unit testing? C#单元测试 - Thread.Sleep(x) - 如何模拟系统时钟 - C# Unit Testing - Thread.Sleep(x) - How to Mock the System Clock 如何在 C# 单元测试项目中模拟 class - How to mock a class in C# unit testing project 在 C# 中进行单元测试时如何正确抽象系统对象? - How to properly abstract system objects when unit testing in C#? 如何在 C# 单元测试中手动存根 function - How to manually stub out a function in C# unit testing 如何在用C#编写的单元测试代码中模拟System.IO.File.ReadAllLines(“ ./ abc.html”) - How to I mock System.IO.File.ReadAllLines(“./abc.html”) in unit test code written c# 如果一个功能依赖于另一个功能,您如何将该内部功能放在模拟数据中以进行单元测试? - If a function depends on another function, how do you emplace that inner-function with mock data for unit testing? 在C#中进行单元测试怎么样? - Unit testing in C# how to? c# - 单元测试、模拟和 ninject 工厂扩展 - c# - unit testing, mock and ninject factory extension
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM