需要:.NET中的文件系统接口和实现

13

可能是重复问题:
如何在C#中模拟文件系统进行单元测试?

我使用Moq作为模拟框架,为我的代码编写单元测试。
我的代码包括对文件系统的调用,使用直接调用System.IO类。例如,File.Exists(...)等。
我想改变这段代码使其更具可测试性,因此应该有一个接口,比如说IFile,有一个相关的方法,比如说Exists(string path)
我知道我可以从头开始编写它,但我想知道是否有一个完整、强大的框架,具有文件系统的接口和实现。这个(期望的)框架也可以是某种“服务”,因此它的API不必是与System.IO命名空间的“接口等效”的。
请注意,我真的很想有接口(而不是静态方法),以便我的代码准备好进行依赖注入。

到目前为止我得到了什么:

  • 在stackoverflow上曾经有一个类似但并非相同的问题
  • codeplex.com网站上有一个名为CodePlex Source Control Clientlink)的项目,其中源代码中包含这些类(请参见源代码中的/Source/TfsLibrary/Utility/以获取具体细节)。

还有其他建议吗?


我为此编写了 https://github.com/guillaume86/VirtualPath(还有其他),它仍然是一个正在进行的项目,API肯定会发生变化,但它已经可以工作了,并且包含了一些测试。 - Guillaume86
6个回答

6

我写了适配器,用于静态的System.IO.FileDirectory方法。然后在我的类中,我会这样做:

public class MyService {
  public IFile File {private get;set;}
  public MyService() {
    File = new FileImpl();
  }
  public void DoSomething() {
    File.ReadAllText("somefile");
  }
}

然后,您可以注入一个模拟的IFile进行测试。

博客文章中链接的 code 似乎不存在了。 - O. R. Mapper

5
好的,我没有你想要的文件系统模拟库(虽然可能有类似的库存在,这会很酷),但是我可以提供一些帮助。"行为派"单元测试学派提出了一个有趣的概念,即"外部接口"。在某些情况下,将对象对整个外部世界的调用转化为一个接口,就像创建外部世界可以调用的方法接口一样,具有同等价值。

在这种情况下,您可以考虑,不是模拟整个文件系统,而是为对象需要的来自外部世界的答案和服务创建一个或多个逻辑上一致的接口。调用仅回答您需要回答的问题...而不是指定实现方式。然后,您可以像您提到的那样使用依赖注入来注入您测试所需的实现。您可能会使用Moq来完成这项工作,因为您已经熟悉它。

因此,您的"外部接口"可能会有一个名为DoesFileExist()的方法。它可能会接受一个路径。或者,如果您的对象正在尝试回答更高级别的业务问题,并且查看文件是否存在只是回答该问题的一种方式,那么您的外部接口可能根本没有关于文件存在的方法。它可能是像"是否出现了对文件服务器的访问"或者甚至是"之前是否保存过游戏"这样的东西。

这需要一些工作,但可能更符合良好单元测试的原则...让您正在测试的对象表达它试图做什么,并让您的单元测试仅测试它。希望这能有所帮助。


这种东西不也被称为“访问者模式”吗? - brendanjerwin
1
但是,当我实现“出口接口”时,我仍然需要与文件系统交互。例如,实现“IsThereAPreviouslySavedGame”方法实际上可能会检查特定文件是否存在。因此,我仍然需要IFile接口,以便可以正确地对实现进行单元测试。 - Ron Klein
不,等一下。我的意思是,“IsThereAPreviouslySavedGame”将是一个布尔方法。你的代码会调用它。对于单元测试,你只需要“注入”一个返回true的模拟实现,并确保该情况下的结果行为是正确的。不需要使用文件系统。 - Charlie Flowers
继续上文,这样,你的单元测试严格关注于你正在测试的单个对象。查看马丁·福勒的文章,了解更多关于这种哲学的内容。我个人认为这个想法有价值,但我并不是完全信奉它。有些情况需要使用它,而有些情况则不需要。 - Charlie Flowers
(再次继续)...这是链接:http://martinfowler.com/articles/mocksArentStubs.html#ClassicalAndMockistTesting 重点关注他所称的“mockist”视角。 - Charlie Flowers
我知道“IsThereAPreviouslySavedGame”返回布尔值。但是,有一个类实现了这个接口,而这个类与文件系统进行交互。为了进行单元测试,我想拥有一个接口,而不是直接使用System.IO命名空间。 - Ron Klein

3
我在CodePlex上维护Jolt.NET项目,其中包含一个库,可以为您生成这些接口及其实现。请参考Jolt.Testing库获取更多信息。

2

0

我了解Typemock 包可以做到这样的事情。


-1
相比嘲讽文件系统接口,我更倾向于模拟文件系统本身。在任何严肃的操作系统上,这都是一个相对容易的任务。你可以使用Mono.Fuse构建一个用户空间文件系统,它是FUSE的C#实现,即User-space文件系统。我不确定是否需要任何移植才能获得Dokan,也就是Windows的FUSE,但是FUSE在Linux、OSX和Solaris上都可以正常工作。

7
真的吗?你实际上试过那个吗?上次我调查在Windows上使用FUSE时,它并不是很成熟。老实说,如果我们模拟文件系统,我认为这将把整个TDD事情推向了一个太极端的地步。 - BobbyShaftoe
“如果我们要模拟文件系统,我认为这已经把TDD的理念过分了。” - Bingo。 - Chris
1
http://www.jprl.com/cgi-bin/gitweb.cgi?p=mono-fuse.git;a=blob;h=832baf9fafc945184547260995d54e1e25a209e0;hb=master;f=src/Mono.Fuse/Mono.Fuse/FileSystem.cs或http://msdn.microsoft.com/en-us/library/system.io.aspx你需要一种自我毁灭的倾向,才能开始这个永无止境的探索。 - rektide

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接