views:

713

answers:

7

We have some methods that call File.Copy, File.Delete, File.Exists, etc. How can we test these methods without actually hitting the file system?

I consider myself a unit testing n00b, so any advice is appreciated.

+3  A: 

If you absolutely have to do this, Typemock Isolator is your friend.

I can't say I've used it myself, and I would try to design my way around it instead, but it'll do the job as far as I'm aware.

Jon Skeet
How might you design your way around this?
Jim
Is Typemock open source?
bovium
@bovium: Nope, it's a commercial product.
Jon Skeet
@Jim: Limit the places where it could possibly interact with the file system, and potentially encapsulate the filesystem interaction in an interface. You could either have a singleton for the filesystem to use (and replace it for test - icky, but not *so* bad) or use dependency injection.
Jon Skeet
But in general, try to make most places that need data take/produce it in the form of streams, readers, writers - don't make anything file-specific unless it really, really has to be.
Jon Skeet
I posted an answer which illustrates "designing around" the static class. In reality, the platform *should* expose a static class with static methods, and when we do OOP we *should* be calling instance methods on interfaces. So we sometimes have to take the time to bridge the gap.
Justice
+1 For the design your way around it - A nice 'complete' answer would perhaps include something like Justice's example showing interfaces, mocking and dependency injection.
David Hall
@David: Yeah - sometimes I just don't have time :(
Jon Skeet
+2  A: 

I would use Moq for this. You would have to create an interface and a class that proxies to the real thing so you could have Moq create an instance of your proxy (a mocked instance) but its the best way to test these sorts of things.

cfeduke
+1  A: 

You can use a mock framework for this and it will create a fake copy of the File object and you can inject the file in the system under test.

I will recommend Rhino Mock.

bovium
There is no File object. We're talking about static methods here. You can design your way around it as described in the comments to my answer, but it does require a change.
Jon Skeet
+13  A: 
public interface IFile {
    void Copy(string source, string dest);
    void Delete(string fn);
    bool Exists(string fn);
}

public class FileImpl : IFile {
    public virtual void Copy(string source, string dest) { File.Copy(source, dest); }
    public virtual void Delete(string fn) { File.Delete(fn); }
    public virtual bool Exists(string fn) { return File.Exists(fn); }
}

[Test]
public void TestMySystemCalls() {
    var filesystem = new Moq.Mock<IFile>();
    var obj = new ClassUnderTest(filesystem);
    filesystem.Expect(fs => fs.Exists("MyFile.txt")).Return(true);
    obj.CheckIfFileExists(); // doesn't hit the underlying filesystem!!!
}
Justice
A: 

I tend to create an interface in most of my projects called IFileController that has all file operations on it. This can have the basic ones, plus any methods that the .NET framework doesn't provide that deal with files.

Using a dependency injection framework, you can get an instance of IFileController without knowing exactly what type it is, and use it without needing to mess around with mocking framework types. This makes everything much more testable, and as a bonus you can change the file storage mechanism without changing your code at all.

On the down side, any new developers need to be told about this interface, otherwise they will just use the .NET methods directly.

Jamie Penney
A: 

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