Simple dependency injection to get you started with unit testing – level 200

More and more people are talking about unit testing, and I’m glad.  There is confusion, however, with unit testing and integration testing, and some folks don’t know there is a difference.  Unit testing is testing code in isolation without exercising code not under test.  Simply put, if I have a class that handles data that is stored in a file, I want to test my custom class without actually exercising the file system.  How do I do that?  Why do I want to do that.  I’ll tackle the why first.  If a test requires that an actual file exists, then you have a pretty significant dependency in your code.  Your code can never run (can never be tested) except by working with the actual file on a file system configured with the correct permissions.  This is fraught with environmental headaches.  Unit tests should be simple and fast.  The unit tests should pass everywhere, not just on one magic environment where everything is painstakingly set up.

Here’s how:  Consider the following code with a test:


    1 using NUnit.Framework;


    2 


    3 namespace DIPSample


    4 {


    5     [TestFixture]


    6     public class DataManagerTester


    7     {


    8         [Test]


    9         public void SaveDate()


   10         {


   11             DataManager manager = new DataManager();


   12             manager.AddData(“Some string data”);


   13             manager.SaveData();


   14         }


   15     }


   16 }




    1 using System;


    2 using System.IO;


    3 


    4 namespace DIPSample


    5 {


    6     class DataManager


    7     {


    8         private string _data;


    9 


   10         public void AddData(string data)


   11         {


   12             _data = data;


   13         }


   14 


   15         public void SaveData()


   16         {


   17             string path = @”C:\dataFile.dat”;


   18             if(!File.Exists(path))


   19                 throw new InvalidOperationException(“Data storage isn’t ready.”);


   20 


   21             //Do something to maintain this data file. . .


   22             //Add info to file, replace data in file, etc.  Use your imagination.


   23         }


   24     }


   25 }


This test seeks to verify that SaveData() does what is is supposed to do.  The problem is that if our file isn’t available the code will fail.  I’m not in a position to configure the environment so that the file is available.  I don’t want to mess with the actual file at this point either.  I’ll need to modify the code so that it’s testable by itself.  The following is the same code modified to be more flexible so that the unit test _is actually a unit test_.  The above test is actually an integration test because its running requires the file system to be on the testing stack.

I define an interface to separate my file system dependency:


    1 namespace DIPSample


    2 {


    3     public interface IDataFileStore


    4     {


    5         void SaveData(string data);


    6     }


    7 }


I make my concrete implementation that calls the file system.  This will be my class for integration and acceptance tests.


    1 using System;


    2 using System.IO;


    3 


    4 namespace DIPSample


    5 {


    6     public class FileStore : IDataFileStore


    7     {


    8         public void SaveData(string data)


    9         {


   10             string path = @”C:\dataFile.dat”;


   11             if(!File.Exists(path))


   12                 throw new InvalidOperationException(“Data storage isn’t ready.”);


   13 


   14             //Do something to maintain this data file. . .


   15             //Add info to file, replace data in file, etc.  Use your imagination.


   16         }


   17     }


   18 }


Here is how my DataManager class changes.  Notice the default constructor and a special constructor specifically for testing:


    1 namespace DIPSample


    2 {


    3     class DataManager


    4     {


    5         private string _data;


    6         private IDataFileStore _fileStore;


    7 


    8         public DataManager()


    9         {


   10             _fileStore = new FileStore();


   11         }


   12 


   13         public DataManager(IDataFileStore _store)


   14         {


   15             _fileStore = _store;


   16         }


   17 


   18         public void AddData(string data)


   19         {


   20             _data = data;


   21         }


   22 


   23         public void SaveData()


   24         {


   25             _fileStore.SaveData(_data);


   26         }


   27     }


   28 }


And in my test below, I use NMock to mock the interface.  It also allows me to verify that the proper method is called with the right data on my interface:


    1 using NUnit.Framework;


    2 using NMock;


    3 


    4 namespace DIPSample


    5 {


    6     [TestFixture]


    7     public class DataManagerTester


    8     {


    9         [Test]


   10         public void SaveDate()


   11         {


   12             IMock mock = new DynamicMock(typeof(IDataFileStore));


   13             mock.Expect(“SaveData”, “Some string data”);


   14 


   15             DataManager manager = new DataManager((IDataFileStore)mock.MockInstance);


   16             manager.AddData(“Some string data”);


   17             manager.SaveData();


   18 


   19             //Verifies SaveData method in the mock is actually called with the right input.


   20             mock.Verify();


   21         }


   22     }


   23 }


This is called Dependency Injection.  You are injecting a dependency to facilitate unit testing.  This decouples your code, and it is now more flexible.  The above is very important because if you can’t do this, then it will be VERY difficult, nay, impossible to do unit testing on a system that uses: files, a database, queues, web services, remoting, {shudder} DCOM, ASP.NET.

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

7 Responses to Simple dependency injection to get you started with unit testing – level 200

  1. BW says:

    awsome example, follows Agile ideas of keep it simple

  2. Kevin Read says:

    This is a great example!

    Things clicked into place after reading this. Cheers.

  3. The constructor isn’t specifically for unit testing. It’s for constructing a generalized version of the DataManager. Better would be

    interface IDataStore {
    void SaveData(string serializationData);
    }
    

    with implementations like

    FileDataStore  : IDataStore
    {
    public void SaveData(string serializationData){
    // do filesystem based storage here, as above
    // check for data file presence, create if it you need it (and can)
    // and so forth, and then store the serialization info
    }
    

    that could be replaced in, for example, a clustered installation by

    SqlServerDataStore : IDataStore
    {
    public void SaveData(string serializationData){
    // do sproc based data management here,
    // doing things like checking for acessibility of the database,
    // transaction wrapping, etc.
    }
    

    And then you can use NMock based unit test as above to verify the logic that is supposed to call SaveData sends in the right state. Load-time configuration to use either the SqlServerDataStore or FileDataStore is then possible, and can be integration tested with confidence that the bits that call SaveData behave in a predictable, tested manner because this unit test covers the question of “When ObjectUnderTest calls IDataStore.SaveData, does it correctly construct its serialization information”. Separate tests would deal with the question of whether SqlServerDataStore.SaveData and FileDataStore.SaveData behave correctly.

    There are a few good books that cover the techniques of Test Driven Development, Beck is fairly definitive, as is Astels.

  4. anon says:

    Since I’ve never written code to be unit tested, it seems very bizarre to me to write a constructor specifically for unit testing, not to mention that weird mock.expect and mock.verify crap. That doesn’t seem intuitive to me at all. What the f*ck is this actually testing? That your code compiles and runs and proves that you are not a complete moron? That’s what a compiler does! I think these blog samples are too light to make any sense or sway me to do things this way for REAL code.

    Also, there needs to be a book and/or website that discusses/walks through the many testing patterns that we will need to use when writing our code for testability. I mean, as developers, there’s really not a whole helluva lot of varying tasks we undertake. We all write apps that connect to databases, get/set data, write to files, consume and validate user-populated forms (win/web) of data, occassionally use multiple threads, and work off of lists/collections to do many of the previous listed things in this sentence.

    I think TDD will be picked up a lot quicker if these test patterns are as widely known as OO principles and patterns. If I had a pattern to follow instead of trying to sift through a bunch of smart guys’ blogs (like yours and Jeremy’s) looking for examples that I can understand and implement when learning to write testable code, that would be an IMMENSE help. As it is, I feel like I’m armed with jack s*hit as I try and muster up enough nerve to start writing unit tests for my code.

    I think of one app I have that just wraps calls to EXEs on the system and monitors failures and retries. It does a bit of multithreading, but it’s just simple calls to other programs and logs stuff to a database. If my unit tests aren’t really TESTING the code to see if it works (since evidently we aren’t supposed to unit test anything having to do with an actual file or going to/from an actual database), what is it actually testing? I read a bunch about how unit tests should be this “document” for your code so that every time you extend (don’t modify!) your system you can see if you broke anything that was previously working? If we really aren’t unit testing to see if the code “works” as intended, then what’s the frickin’ point?!

  5. John,
    You are exactly right about NMock. It will (at runtime) create a custom object instance to satisfy an interface, abstract class, or override virtual methods on a concrete class.

    Testing the database is very hairing, and it’s not conducive to unit testing. That’s why it’s a good idea to keep the stored procedures very thin. You’ll need integration tests to go all the way through the database, and that requires putting the database in a known state for _every_ test. Testing in managed code is much easier.

  6. johnpapa says:

    Interesting example, Jeffrey. I am not familiar with NMock, but it looks like it is used to “mock” a class’ behavior. I’ll check that out, thanks. Jeremy and I had a brief conversation a few weeks ago about how to make unit tests truly “unit tests” in a data retreival situation. For example, when you want to test:

    public Customer GetData(int customerID)
    { // … get some data by hitting a database and return it … }

    A few basic tests might be making sure that you can retrieve a valid Customer, which means you should know a valid ID (or be able to get one). All of which leads to knowing the database state … thus its not truly unit testing IMO. As I have said, I am not familiar with NMock but it makes me think that I could employ this technique to break the test out to not have to hit the database and to return a Customer object without knowing a valid ID.

    OK, so I am thinking out loud here … but that’s what blogs are for. :-)

  7. Jeremy D. Miller says:

    Nice example.

Leave a Reply