Test Driven Development with Static Framework Classes
 

In a recent development effort, I was developing some code that had to interact with data from the file system.  Simple enough, except that the File and  Directory are static, and do not lend themselves to mocking. 

The domain for this sample is very straight forward:

  • Read XML from a manifest
  • Populate some domain objects from the XML

The project had a lot more in it, of course, but that is enough of a problem space to show this solution.

I knew that I needed to store some XML in a file to represent the current versions of files in an application.  I didn’t know exactly what data I needed.  So, I started by writing the process that would parse the data into an IList<ServerFileInfo>. This was easy, as I wrote a parse method that took XML and return the correct list. API shown here:

IList<ServerFileInfo> ParseVersionXML(string versionXML);

The problem with this API as I flushed out my other code was that it relied on each and every call to this method to have already opened the file and read the contents.  It made more sense to have an API like this:

IList<ServerFileInfo> ParseVersionXML(string fileName);

That cleaned up my code, but broke my tests. If I was sending in the file name, this method then had to check to make sure that the file exists and then read from it. Both of these tasks require the File class.


In custom code, we would solve this with dependency injection, and change the API to something along these lines:

ILIst<ServerFileInfo> ParseVersionXML(IFile file, string fileName);

Of course, this won’t work because of the implementation of the File class in the framework. So I refactored my tests to include my new Interface:

string xmlString = "<foo/>";
var fileWrapper = MockRepository.GenerateStub<IFileWrapper>();
fileWrapper.Stub(x => x.ReadFile(""))
    .IgnoreArguments()
    .Return(xmlString);
IVersionLoader sut = new VersionLoader();
IList<ServerFileInfo> files = 
    sut.ParseVersionXML(fileWrapper,xmlString);

My new Interface is defined like this:

public interface IFileWrapper
{
    string ReadFile(string pathAndFileName);
}

I then refactored my line of business code like this:

public IList<ServerFileInfo> ParseVersionXML(
    IFileWrapper fileWrapper, string fileName)
{
   XDocument doc = 
        XDocument.Load(
             new StringReader(
                 fileWrapper.ReadFile(fileName)));
   //Create List from XML
   return list;
}

I removed my dependency on the static File class, and all of my tests once again passed.


Heppy Coding!


Comments are closed

.NET Musings

Wandering thoughts of a developer, architect, speaker, and trainer

Managed Windows Shared Hosting by OrcsWeb