14

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.

Jim
  • 11,229
  • 20
  • 79
  • 114
  • What restricts you from hitting the file system? – grieve Nov 05 '08 at 21:33
  • @grieve--from what I've read, unit tests should not hit the file system, db, or go accross the network. I'm trying to stick to those rules. – Jim Nov 05 '08 at 21:35
  • Also, see http://stackoverflow.com/questions/106766/unit-testing-file-modifications – S.Lott Nov 05 '08 at 21:53
  • @Jim: the "don't hit the filesystem" is a bendable rule. As long as you can assure that the file system fixture is consistent, hitting the file system is fine. Assuring it's consistent may be little more than a restore script on the test directory. – S.Lott Nov 05 '08 at 21:55

6 Answers6

23
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!!!
}
yfeldblum
  • 65,165
  • 12
  • 129
  • 169
3

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
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • How might you design your way around this? – Jim Nov 05 '08 at 21:32
  • @bovium: Nope, it's a commercial product. – Jon Skeet Nov 05 '08 at 21:37
  • @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 Nov 05 '08 at 21:38
  • 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 Nov 05 '08 at 21:39
  • 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. – yfeldblum Nov 05 '08 at 21:44
  • +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 Nov 05 '08 at 21:47
  • @David: Yeah - sometimes I just don't have time :( – Jon Skeet Nov 05 '08 at 22:10
2

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
  • 23,100
  • 10
  • 61
  • 65
0

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
  • 19,700
  • 9
  • 74
  • 90
0

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
  • 2,869
  • 6
  • 25
  • 35
  • 1
    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 Nov 05 '08 at 21:39
0

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
  • 9,424
  • 3
  • 29
  • 37