12

Possible Duplicate:
How do you mock out the file system in C# for unit testing?

I write unit tests to my code, using Moq as a mocking framework.
My code includes calls to the file system, using direct calls to System.IO classes. For instance, File.Exists(...) etc.
I'd like to change that code to be more testable, so I should have an interface, say IFile, with a relevant method, say Exists(string path).
I know I can write it from scratch, but I thought that maybe there's a complete, robust framework that has both interfaces and implementations for the file system. This (desired) framework may also be some kind of a "service", and therefore its API doesn't have to be an "interface equivalent" to the System.IO namespace.
Note that I'd really like to have interfaces (and not static methods) so that my code is ready for dependency injection

What I got so far:

  • A somehow similar, yet not the same question was asked in stackoverflow
  • In codeplex.com there's a project named CodePlex Source Control Client (link) that has such classes in the source code (see /Source/TfsLibrary/Utility/ in the source code for specific details)

Any other recommendations?

Community
  • 1
  • 1
Ron Klein
  • 8,650
  • 9
  • 50
  • 83
  • I wrote https://github.com/guillaume86/VirtualPath for that purpose (and more), it's still WIP and the API will certainly change but it already works, and some tests are included. – Guillaume86 Oct 30 '13 at 20:23

6 Answers6

6

I've written adapters for the static System.IO.File and Directory methods. Then in my classes I do the following:

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

Then you can inject a mock as IFile for testing.

Mauricio Scheffer
  • 96,120
  • 20
  • 187
  • 273
5

Alright, I don't have the file-system-mock library you wish for (though it may be out there somewhere and would be cool), but this may help. One of the interesting concepts the "behaviorist" school of unit testing thought has come up with is the idea of an "outgoing interface". In some cases, there seems to be as much value in taking the calls an object makes to the entire universe of things outside of itself, and turning them into an interface, as there is in the typical act of making an interface for methods the outside world can call on your object).

In this case, you might consider, instead of mocking the entire file system, making one or more logically coherent interfaces for the answers and services your object needs from the outside world. The calls would merely answer the questions you need answered ... not dictate implementation. Then, you can use dependency injection as you mentioned to inject in the implementation you desire for your tests. And you'll probably use Moq to do so since you're familiar with it.

So, your "outgoing interface" might have a method called DoesFileExist(). It might accept a path. Or, if there is a "higher level" business question your object is trying to answer, and seeing if a file exists is merely the way that question is answered, then your outgoing interface might not have a method about file existence at all. It might instead be something like "DoIAppearToHaveAccessToTheFileServer", or even "IsThereAPreviouslySavedGame".

This is some work, but might be truer to the principles of good unit testing ... let your object-under-test express what it is trying to do, and let your unit tests test it and only it. Just a thought ... hope it helps.

Charlie Flowers
  • 16,782
  • 8
  • 69
  • 86
  • Isn't this sort of thing also called the "visitor pattern"? – brendanjerwin Mar 20 '09 at 14:24
  • 1
    But still, when I implement the "outgoing interface", I have to interact with the file system. For instance, implementing the "IsThereAPreviouslySavedGame" method might actually check if a specific file exists. So I still need the IFile interface, so I can unit-test the implementation properly. – Ron Klein Mar 20 '09 at 21:56
  • No, wait. What I mean is, "IsThereAPreviouslySavedGame" would be a boolean method. And your code would call it. For the unit test, you would merely "inject" a mock implementation that returns true, and make sure the resulting behavior was correct for that case. No need for the file system. – Charlie Flowers Mar 21 '09 at 04:40
  • (Continuing) ... this way, your unit test focuses **strictly** on the one single object you're testing. Check out Martin Fowler's article for more about this philosophy. I personally think the idea has merit, but I don't use it religiously. Some situations call for it and others don't. (more...) – Charlie Flowers Mar 21 '09 at 04:43
  • (Continuing again) ... Here's the link: http://martinfowler.com/articles/mocksArentStubs.html#ClassicalAndMockistTesting Focus on what he calls the "mockist" point of view. – Charlie Flowers Mar 21 '09 at 04:45
  • I understand that "IsThereAPreviouslySavedGame" returns Boolean. Still, there is a class that *implements* this interface, and this class interacts with the file system. In order to UT it, I'd like to have an interface, rather than using System.IO namespace directly. – Ron Klein Mar 22 '09 at 21:24
3

I maintain the Jolt.NET project on CodePlex, which contains a library to generate such interfaces and their implementations for you. Please refer to the Jolt.Testing library for more information.

Steve Guidi
  • 18,493
  • 8
  • 68
  • 86
2

http://systemwrapper.codeplex.com/ offers a good selection, although I haven't tried it yet. It looks like the author could use some help with completing the project.

TrueWill
  • 23,842
  • 7
  • 88
  • 133
0

I understand that the Typemock package can do things like this.

Steve Guidi
  • 18,493
  • 8
  • 68
  • 86
Preet Sangha
  • 61,126
  • 17
  • 134
  • 202
-1

Rather than mock the file system interface, I'd traditionally opt to mock the Filesystem itself. This is a relatively easy task on any serious OS. You can build a userspace filesystem very easily with Mono.Fuse, a C# implementation of FUSE, Filesystems in USErspace. I'm not sure what if any porting would be required to get Dokan, FUSE for Windows, but FUSE works fine on Linux, OSX, and Solaris.

rektide
  • 946
  • 7
  • 17
  • 7
    Really? Have you actually tried that? Last time I looked into FUSE on Windows it was not very mature at all. Honestly, I think this is taking the whole TDD thing way too far if we are mocking the filesystem. – BobbyShaftoe Mar 19 '09 at 23:39
  • "I think this is taking the whole TDD thing way too far if we are mocking the filesystem." - Bingo. – Chris Mar 20 '09 at 20:33
  • 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 or http://msdn.microsoft.com/en-us/library/system.io.aspx You'd need a penchant for self destruction to start that never ending quest. – rektide Mar 23 '09 at 19:18