Sunday, March 16, 2008

Injectable file adapters

Someone must have done this, but I really couldn't find it. I'm talking about an IoC-friendly System.IO.File replacement. You know, unit tests shouldn't touch the file system, etc. So if your code writes a file and want to unit-test it, you pretty much have to mock the writing of the file. Except there's a known problem, System.IO.File is a static class, and those are not mockable. Even TypeMock can't mock System.IO.File since it's part of mscorlib. So the only solution left is to build an interface and a wrapper around File. So, here's the source, probably the most boring code I've ever written. I've also included a static locator that gets the IFile implementation using Ayende's IoC static accessor to the Windsor Container, so you can write code like this:

public void Copy() {
    var mocks = new MockRepository();
    var container = mocks.CreateMock<IWindsorContainer>();
    var fileImpl = mocks.CreateMock<IFile>();
    With.Mocks(mocks).Expecting(delegate {
        Expect.Call(() => fileImpl.Copy(null, null))
    }).Verify(delegate {
        FileEx.Copy("source", "dest");                

So you only have to replace your calls to System.IO.File to FileEx and that's it. You get the benefits of testability and extensibility (yes, sometimes you need to provide a different behavior for File.WriteAllText()) and you don't have to deal with interfaces, implementations, etc once you have set up the container. Personally, I prefer to make explicit the dependency for IFile in my components.

Like I said, I was very surprised that I couldn't find a working implementation of this... specially because there was a big debate a year ago about maintenability, YAGNI, dependency injection, coupling and more, and Anders NorĂ¥s commented this solution on one of his own posts.

Well, I hope someone finds this useful.


RedGreenRefactor said...

Nice, also check jayflowers doubler.

David Keaveny said... does a pretty comprehensive job of wrapping most of System.IO and quite a few of the other System namespaces.

mausch said...

@David Keaveny: indeed, now there are a couple more that do this, but take a look at the dates.

Anonymous said...

I still like the simplicity of what you have written after reviewing my options. Thanks again for sharing.


Stefan said...

FYI, the last version of JustMock can mock every method in mscorlib.

~K said...

I've pushed this into nuget as mscorlib-mock (

Mauricio Scheffer said...

@Kori : nice!

Gavin S said...

~K Why the Castle Windsor dependency ?