Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create testable code using .Net IO classes?

I want to create unit testable code that mocks out the calls to the .Net System.IO classes, so I can really unit test instead of depending on the filesystem. I am using the SystemWrapper classes to wrap around the BCL classes.

I am trying to get a simple example working to see whether a file exists.

The problem I am having is that injecting the dependency in the class doesn't work because instantiating the dependency (through StructureMap) requires knowing what constructor parameter to pass, which won't be available at that time, also there is no default constructor.

sample code:

// don't want to create dependency here like so
//IFileInfoWrap fileInfoWrap = new FileInfoWrap(filename);

// using service locator (anti-pattern?!) since it can't be 
// injected in this class
var fileInfoWrap = ObjectFactory.GetInstance<IFileInfoWrap>(
    new ExplicitArguments(new Dictionary<string, object>
    {
        {"fileName", filename}
    }));

Console.WriteLine("File exists? {0}",  fileInfoWrap.Exists); 

What I don't like is that the dependency is not injected, ObjectFactory should not be here (but I see no other way of creating this). The ExplicitArguments makes it messy and the argument-name is a magic-string.

For me to get this to work StructureMap config class needs to know explict which constructor I want to use ( I just started with StructureMap so this might not be the right way to set it up):

ObjectFactory.Initialize(x =>
{
    x.Scan(scan =>
    {
        scan.AssembliesFromPath(".");
        scan.RegisterConcreteTypesAgainstTheFirstInterface();
        scan.WithDefaultConventions();
    });

    // use the correct constructor (string instead of FileInfo)
    x.SelectConstructor(() => new FileInfoWrap(null as string));

    // setting the value of the constructor
    x.For<IFileInfoWrap>()
        .Use<FileInfoWrap>()
        .Ctor<string>("fileName")
        .Is(@".");
});

Does anyone found a better solution to create testable code against the System.IO classes? I know part of the problem is in the design of the System.IO classes.

like image 299
Michiel Vlootman Avatar asked Jan 07 '12 09:01

Michiel Vlootman


1 Answers

An approach I've used very successfully is to roll my own proxy types for the types found in System.IO and other parts of the FCL. E.g. I want to take a dependency on System.IO.File. I create a library called System.IO.Proxies and add a concrete type File and an interface IFile. The interface IFile exposes members equivalent to all those which I require from System.IO.File and the concrete type implements those members by doing nothing other than forwarding method calls to System.IO.File. System.IO.Proxies is excluded from unit testing and code coverage. In my consuming assembly, I take a dependeny only on System.IO.Proxies and, specifically, I only take a dependency on IFile. This way I can mock this dependency easily and attain 100% code coverage for my consuming assembly.

(Note that this is a tailored version of my more general answer to a previous question.)

like image 168
Adam Ralph Avatar answered Nov 16 '22 21:11

Adam Ralph