Needed: File system interfaces and implementation in .NET [duplicate]
Asked Answered
C

6

13

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?

Conjunctive answered 19/3, 2009 at 22:15 Comment(1)
I wrote 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.Emma
I
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.

Indeclinable answered 20/3, 2009 at 2:46 Comment(1)
The code linked to in the blogpost does not seem to exist any more.Threefold
U
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.

Ur answered 20/3, 2009 at 2:29 Comment(6)
Isn't this sort of thing also called the "visitor pattern"?Yggdrasil
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.Conjunctive
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.Ur
(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...)Ur
(Continuing again) ... Here's the link: martinfowler.com/articles/… Focus on what he calls the "mockist" point of view.Ur
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.Conjunctive
W
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.

Wistful answered 6/7, 2009 at 23:39 Comment(0)
B
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.

Brooks answered 18/11, 2010 at 15:32 Comment(0)
S
0

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

Sussna answered 19/3, 2009 at 22:24 Comment(0)
A
-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.

Aeolipile answered 19/3, 2009 at 22:33 Comment(3)
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.Paquette
"I think this is taking the whole TDD thing way too far if we are mocking the filesystem." - Bingo.Bonfire
jprl.com/cgi-bin/… or msdn.microsoft.com/en-us/library/system.io.aspx You'd need a penchant for self destruction to start that never ending quest.Aeolipile

© 2022 - 2024 — McMap. All rights reserved.