Dependency Injection for Test Driven Development

As applications grow more and more complex, they cost more money to develop, test and maintain. Each bug that leaves the development stage, needs the QA’s effort to identify and notify the developer who again spends time to fix it and get it signed off from the QA. Hence its imperative that good code is written the first time around. This fundamental need is what gave birth to what we call Test Drive Development (TDD), i.e. where the developer writes an automated test case first, sees it failing and then writes the code for it to verify the test passes. This technique is also called the red-green style of development.

Automated testing is an integral part of Test Driven Development. A developer simply doesn’t have the time to do functional unit testing of his application, an effort which would neither be reproducible, nor comprehensive. Hence developers use Automated testing tools which decrease the testing effort, even if a little bit more code needs to be written.

The biggest problem while automating testing for applications, especially web applications is the tight coupling between layers making it impossible to test one layer without the another. This can create a lot of complications, since you might get false positives and negatives. Imagine a scenario where you write a test case for a business layer method which retrieves some data from the database and processes it. The aim here is to only test the business layer’s functionality, but if there is a concrete dependency on the data layer, then the result of the test would inevitably depend on the retrieval of data. This is where you could get false positives or negatives.

Hence the reasoning that the test cases should be limited to only the functionality being tested. Consider the below code.

    //Your Business Class
    class BusinessObjects
    {
        //This is a concrete dependency on the dataLayer Object
        private DataObjects _dataLayerObj;
 
        public BusinessObjects()
        {
            //Initializing the DataLayer object
            _dataLayerObj = new DataObjects();
        }
 
        //Get the customer's Name in upper case
        public string GetCustomerNameInUpperCase(int _customerID)
        {
            return (string)_dataLayerObj.RunScalarQuery("Your Select Query here").ToUpper();
        }
    }

So If you write a test on the GetCustomerNameInUpperCase method, it would need to have the database up and running in order to pass. To avoid this, a technique called dependency injection is used in which the application’s layers are loosely coupled with each other enabling us to test each layer’s functionality independent of each other.

This is accomplished by introducing an interface from which the Data layer would inherit and do its work. The business layer would be aware only of these interfaces. Business layer would no longer care about the implementation and thus would allow us to define our own implementation of the interface. This implementation can be “injected” through an additional constructor. Lets see the code for the data layer now.

    //This is the Interface on which the business layer
    //would depend and not the data layer class itself
    interface IData
    {
        public object RunScalarQuery(string _selectQuery);
    }
 
    //Other code is oblivious to the implementation behind
    //the data layer as long as it returns the name
    class DataObjects: IData
    {
        //Implementation of the interface
        public object RunScalarQuery(string _selectQuery)
        {
            //Do all the database related stuff
            return "Some Customer's Name";
        }
    }

Now we would need to modify the business layer to make it depend on the interface.

   class BusinessObjects
    {
        //It now depends on the interface
        private IData _dataLayerObj;
 
        public BusinessObjects()
        {
            //Initializing the DataLayer object
            _dataLayerObj = new DataObjects();
        }
 
        //This is the additional constructor that is used
        //to inject a custom implementation
        public BusinessObjects(IData _dataObject)
        {
            this._dataLayerObj = _dataObject;
        }
 
        //Get the customer's Name in upper case
        public string GetCustomerNameInUpperCase(int _customerID)
        {
            return ((string)_dataLayerObj.RunScalarQuery("Your Select Query here")).ToUpper();
        }
    }

Now lets write the test case for this method. Instead of using the default constructor making the Business layer go to the database for the customer’s name, our very own Mock Class does the trick by supplying a dummy value.

    [TestFixture]
    public class BusinessLayerTests
    {
        BusinessObjects _bObjects;
 
        [SetUp]
        public void Init()
        {
            //Pass our own implementation of IDATA using
            //the additional constructor we defined
            _bObjects = new BusinessObjects(new MockClass());
        }
 
        [Test]
        public void CheckIfUserNameIsUpper()
        {
            //User regular Expressions to make sure all upper cases are returned
            string _returnedName = _bObjects.GetCustomerNameInUpperCase(2342);
            Console.WriteLine(_returnedName);
            Assert.IsTrue(Regex.IsMatch(_returnedName, "^[A-Z]+$"));
        }
    }
 
    //This inherirs from IData
    public class MockClass : IData
    {
        #region IData Members
 
        public object RunScalarQuery(string _selectQuery)
        {
            //Simply return a lower case name
            return "lowercaseName";
        }
 
        #endregion
    }
 
}

This does seem like too much work to test such a simple functionality, but believe me, the benefits grow exponentially with the size of the project. As you start following this approach, it would be easier to test as you go, making the chances of missing out on that tiny bug much lesser. And at the end of the day, nothing makes the developer more happy than seeing all his unit tests pass with the large green NUnit bar.

4 comments to Dependency Injection for Test Driven Development

  • Hi Ganesh,

    Really nice post.

    However, I have a doubt. As shown in the example, the BusinessObjects and DataObjects classes will be tested individually. With this methodology, there is no test in place to check how the BL works combined with the DL. Or does this portion has more to do with manual testing?

    Krupa

  • The timezone of this blog is configured to be US time? I wish I could wake up and post comments the same time in India… that will be a feast day.

  • Thanks for pointing this out Krupa, I have changed the timing to India Time. I had never paid attention to this before. :)

  • Hi Krupa,
    A core idea of Test driven development is to have the developer write code for the unit test along with the functionality. So you write a stub for the method and then the test case for it and see the test fail. then you code the functionality and make sure it passes. This makes each method of your application work as expected. However when you combine more than one Layer in this testing process, external dependencies are introduced and your code may fail because of the database not working as expected. Hence the suggestion to build loosely coupled layers that can be tested independently. The QA would anyway functionally test the entire application to make sure all layers work together.

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">