# Thursday, July 03, 2008
« Rhino Mocks Logging | Main | Happy 5th of July »

Sometimes you'll actually have a class that needs to read and parse files on disk.  This is pretty easy to do with the FCL StreamReader, but the problem is how do we test a class that is accessing resources on disk like text files?

Most devs first reaction is to create a file on disk in the test project and read that in.  This is often better than nothing, but has some problems associated with it.  Your test assembly now requires physical files on disk which are probably not in the current running test directory, or maybe you've moved the entire assembly to a different location then the test expected.  The real issue is that the class under test controls where the data is read from, not the test.

In short, these external files make your tests brittle, slower, and less intention revealing.  Not only that, but the external dependency really makes the test an integration test and less of a unit test.

The normal solution is to hide the external dependency behind a facade or interface that is mockable.  Unfortunately this is difficult because access to files on disk normally is very short lived.  A StreamReader instance often only lives for the duration of a method call, if even that.  The typical usage pattern  is: construct/open, read, close.

   1: using (var reader = new StreamReader(@"c:\file.txt"))
   2: {
   3:     fileType = reader.ReadLine();
   4: }

We really can't just inject a StreamReader instance into our class under test because the StreamReader opens the file via its constructor.  The problem is that the class under test is responsible for the entire StreamReader life cycle.

The simplest way around this is to use a factory class to create StreamReader instances which allows us to control the instantiation of the StreamReader, which is important when we go to unit test.

   1: public class StreamReaderFactory : IStreamReaderFactory
   2: {
   3:     public StreamReader CreateReader(string path)
   4:     {
   5:         return new StreamReader(path);
   6:     }
   7: }
   8:  
   9: public interface IStreamReaderFactory
  10: {
  11:     StreamReader CreateReader(string path);
  12: }

With that interface in place we can then inject a StreamReaderFactory implementation into our class when running in production. The factory couldn't be any simpler, its just a simple wrapper around the new statement. 

Now when we unit test we'll swap out the very simple factory implementation with a mocked one using RhinoMocks.  This mocked factory will return a predefined result which is located at the beginning of the test, keeping everything located in the test.

   1: [Test]
   2: public void Should_parse_resource_type_from_first_line()
   3: {
   4:     using (var content = CreateStream("F4", "some text resource"))
   5:     {
   6:         var streamReaderFactory = MockRepository.GenerateStub<IStreamReaderFactory>();
   7:         streamReaderFactory.Stub(x => x.CreateReader(@"resource.dat"))
   8:             .Return(new StreamReader(content));
   9:  
  10:         var classUnderTest = new ResourceParser(streamReaderFactory);
  11:         classUnderTest.ParseResources();
  12:  
  13:         Assert.AreEqual("F4", classUnderTest.ResourceType);
  14:     }         
  15: }
  16:  
  17: private static Stream CreateStream(params string[] lines)
  18: {
  19:     string rawContent = string.Join(Environment.NewLine, lines);
  20:     return new MemoryStream(Encoding.UTF8.GetBytes(rawContent));
  21: }

If we had a really complicated external file I would more than likely stick it into an embedded resource text file, but that's only if the file was complicated or a pain to create in code as a string.  The point is with the factory in place the test controls where the data comes from, not the class under test.

Comments are closed.