Unit testing a class that retrieves information from a file

March 1st, 2009 by ganton | Print

Sometimes we should develop a class that will load some information from a file and to store this information in some object-oriented form in order to be further used by other application classes. In this post I’ll try to share with you how I’ve developed such a class for my SQLCEPad tool using TDD practices.

Let’s start with what I have as requirements. I need a class that should read a text file with SQL keywords from application directory, should parse the file, and should store retrieved keywords into a hash table for further fast access. Analyzing these requirements I’ve decided that I need a class with a property of type Dictionary<string, string> named Keywords and a public method that will load the data from the file into Keywords property. Having this in mind I’ve coded an initial test as below.

   1: [TestFixture]
   2: public class SqlKeywordsManagerTestFixture
   3: {
   4:     private string selectKeyword = "SELECT";
   5:     private string insertKeyword = "INSERT";
   6:     private string updateKeyword = "UPDATE";
   7:     private string deleteKeyword = "DELETE";
   8:  
   9:  
  10:     public Dictionary<string, string> Keywords { get; set; }
  11:  
  12:     [TestFixtureSetUp]
  13:     public void TestFixtureSetUp()
  14:     { 
  15:         Keywords = new Dictionary<string,string>();
  16:         Keywords.Add(selectKeyword, selectKeyword);
  17:         Keywords.Add(insertKeyword, insertKeyword);
  18:         Keywords.Add(updateKeyword, updateKeyword);
  19:         Keywords.Add(deleteKeyword, deleteKeyword);
  20:     }
  21:  
  22:     [Test]
  23:     public void LoadTest()
  24:     {
  25:         Assert.AreEqual(selectKeyword, Keywords[selectKeyword]);
  26:         Assert.AreEqual(insertKeyword, Keywords[insertKeyword]);
  27:         Assert.AreEqual(updateKeyword, Keywords[updateKeyword]);
  28:         Assert.AreEqual(deleteKeyword, Keywords[deleteKeyword]);
  29:     }
  30: }

This test implements a Keywords that will be used for storing SQL keywords. Then in set up fixture method Keywords property is initialized and filled in with test data. Preparing this I was able to run the test method LoadTest in green.

First implementation of the real class that came into my mind was to create a simple Load method that will do all the work. But it is not the best way to implement such a functionality. Do you see the obvious? Yes, if I choose this implementation the Load method will do more than it is supposed to do. In addition, I’ll not be able to test it without accessing some real file. For sure, I can create a test file for the test in its set up method and delete it after the test in its tear down method but it looks more like an integration test than as a unit test.

The second variant in this case is to separate file accessing and reading and loading data to Keywords property. I suggested that such implementation is much more suitable for my solution. For this variant I needed to interfaces. You can see them below.

   1: public interface IFileLoader
   2: {
   3:     StreamReader LoadStream();
   4: }
   5:  
   6: public interface ISqlKeywordsManager
   7: {
   8:     Dictionary<string, string> Keywords { get; }
   9:     void Load(IFileLoader loader);
  10: }

IFileLoader implementations should encapsulate the logic of opening the file and should return a StreamReader. A mock object of IFileLoader will allow me to return a StreamReader with test information that will be used by ISqlKeywordsManager. ISqlKeywordsManager contains all logic that is required to retrieve data from the stream and to populate Keywords with the data.

It was the time to extend the unit test fixture. I’ve created a method that is responsible for creating a MemoryStreaam with test data. In addition, I’ve extended LoadTest method to check if the stream was properly created and that it contains correct data. The new test fixture is shown below.

   1: [TestFixture]
   2: public class SqlKeywordsManagerTestFixture
   3: {
   4:     private string selectKeyword = "SELECT";
   5:     private string insertKeyword = "INSERT";
   6:     private string updateKeyword = "UPDATE";
   7:     private string deleteKeyword = "DELETE";
   8:  
   9:     public Dictionary<string, string> Keywords { get; set; }
  10:  
  11:     private MemoryStream CreateTestData()
  12:     {
  13:         MemoryStream resultStream = null;
  14:         using (MemoryStream ms = new MemoryStream())
  15:         {
  16:             using (StreamWriter sw = new StreamWriter(ms))
  17:             {
  18:                 sw.WriteLine(selectKeyword);
  19:                 sw.WriteLine(insertKeyword);
  20:                 sw.WriteLine(updateKeyword);
  21:                 sw.WriteLine(deleteKeyword);
  22:                 sw.Flush();
  23:  
  24:                 ms.Seek(0, SeekOrigin.Begin);
  25:                 resultStream = new MemoryStream(ms.ToArray());
  26:             }
  27:         }
  28:         return resultStream;
  29:     }
  30:  
  31:     [TestFixtureSetUp]
  32:     public void TestFixtureSetUp()
  33:     { 
  34:         Keywords = new Dictionary<string,string>();
  35:         Keywords.Add(selectKeyword, selectKeyword);
  36:         Keywords.Add(insertKeyword, insertKeyword);
  37:         Keywords.Add(updateKeyword, updateKeyword);
  38:         Keywords.Add(deleteKeyword, deleteKeyword);            
  39:     }
  40:  
  41:     [Test]
  42:     public void LoadTest()
  43:     {
  44:         using (MemoryStream ms = CreateTestData())
  45:         {
  46:             using (StreamReader sr = new StreamReader(ms))
  47:             {
  48:                 Assert.AreEqual(selectKeyword, sr.ReadLine());
  49:                 Assert.AreEqual(insertKeyword, sr.ReadLine());
  50:                 Assert.AreEqual(updateKeyword, sr.ReadLine());
  51:                 Assert.AreEqual(deleteKeyword, sr.ReadLine());
  52:             }
  53:         }
  54:         Assert.AreEqual(selectKeyword, Keywords[selectKeyword]);
  55:         Assert.AreEqual(insertKeyword, Keywords[insertKeyword]);
  56:         Assert.AreEqual(updateKeyword, Keywords[updateKeyword]);
  57:         Assert.AreEqual(deleteKeyword, Keywords[deleteKeyword]);
  58:     }
  59: }

Having all this staff created I’ve implemented ISqlKeywordManager and change the LoadTest method. I’ve also re-factored the test fixture in order to remove duplicated data that was not needed anymore. The final test fixture is shown below.

   1: [TestFixture]
   2: public class SqlKeywordsManagerTestFixture
   3: {
   4:     private string selectKeyword = "SELECT";
   5:     private string insertKeyword = "INSERT";
   6:     private string updateKeyword = "UPDATE";
   7:     private string deleteKeyword = "DELETE";
   8:  
   9:     private MemoryStream CreateTestData()
  10:     {
  11:         MemoryStream resultStream = null;
  12:         using (MemoryStream ms = new MemoryStream())
  13:         {
  14:             using (StreamWriter sw = new StreamWriter(ms))
  15:             {
  16:                 sw.WriteLine(selectKeyword);
  17:                 sw.WriteLine(insertKeyword);
  18:                 sw.WriteLine(updateKeyword);
  19:                 sw.WriteLine(deleteKeyword);
  20:                 sw.Flush();
  21:  
  22:                 ms.Seek(0, SeekOrigin.Begin);
  23:                 resultStream = new MemoryStream(ms.ToArray());
  24:             }
  25:         }
  26:         return resultStream;
  27:     }
  28:  
  29:     [Test]
  30:     public void LoadTest()
  31:     {
  32:         MockRepository mocks = new MockRepository();
  33:         IFileLoader loader = mocks.StrictMock<IFileLoader>();
  34:         Expect.Call(loader.LoadStream()).Return(new StreamReader(CreateTestData()));
  35:         mocks.ReplayAll();
  36:         SqlKeywordsManager manager = new SqlKeywordsManager();
  37:         manager.Load(loader);
  38:         Assert.AreEqual(selectKeyword, manager.Keywords[selectKeyword]);
  39:         Assert.AreEqual(insertKeyword, manager.Keywords[insertKeyword]);
  40:         Assert.AreEqual(updateKeyword, manager.Keywords[updateKeyword]);
  41:         Assert.AreEqual(deleteKeyword, manager.Keywords[deleteKeyword]);
  42:     }
  43: }

In this code samples I was using NUnit Framework and Rhino.Mocks Framework.

One Response to “Unit testing a class that retrieves information from a file”

  1. Anton Gochev’s Weblog » Another way to unit test a class that retrieves information from a file Says:

    [...] Similar PostsUnit testing a class that retrieves information from a file [...]

Leave a Reply