July 22, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Writing More Testable Code with Dependency Injection

  • October 6, 2006
  • By Daniel Gartner
  • Send Email »
  • More Articles »

Dependency injection is a simple design pattern that can be used to improve the testability of code by abstracting the dependencies out of a class and transferring control of its dependencies to the client code that uses the class (inversion of control). When I use the term dependency in this article, it can mean any member variable of a class. Take the following class diagram for a PropertyManager class, for example.

Figure 1: PropertyManager's Dependencies

As you can see, the PropertyManager has two private fields, dataManager and logger of type DbPropertyDataManager and Logger, respectively. Since the PropertyManager class uses those instance variables to perform its methods, it can be said they are both dependencies of the PropertyManager class. Presumably, DbPropertyDataManager writes to a database, and Logger writes to some external log file. If I'm unit testing this PropertyManager class, I don't need to perform any of these file or database operations to verify that the PropertyManager works correctly. In fact, I don't want to use the DbPropertyDataManager or Logger classes at all. I want to use a mock or a stub instead. So, it would be better to develop the PropertyManager class such that the dependencies are settable, either through code or an external configuration file. In a nutshell, this is dependency injection: allowing for the specific implementation of a dependency (a class in a "uses" relationship) to be settable (injected).

Dependency injection allows you to better isolate your units when testing your application. External dependencies of a class, such as file, database or network I/O operations, can be abstracted at test time with a different implementation of the dependency.

I'm going to illustrate dependency injection using a Property Management application used to store and retrieve the details of various real estate properties. The PropertyInfo class is a simple data container that stores the details of a particular property.

Figure 2: PropertyInfo Class Diagram

PropertyManager will be used to handle the business logic of the application and DbPropertyDataManager will handle the storing and retrieval of properties from a data store. My initial design calls for PropertyManager to use DbPropertyDataManager to add, update, delete and retrieve PropertyInfo objects from a SQL 2005 database. However, this would lead to some problems during testing. As you may know, database testing can be a pain, and I want to develop this application as painlessly as possible. So, instead of having PropertyManager use DbPropertyDataManager directly, I'll create an interface, IPropertyDataManager that acts as the contract for all of my data related operations (see Listing 1) as well as two separate classes that implement IPropertyDataManager, DbPropertyDataManager and ListPropertyDataManager. DbPropertyDataManager talks to the SQL 2005 database, while ListPropertyDataManager is a simpler implementation that uses an ArrayList as its in-memory data store. This way, during unit testing, I can use the ListPropertyDataManager implementation to isolate the PropertyManager without being dependent on an external SQL 2005 database.

Listing 1: IPropertyDataManager Interface

public interface IPropertyDataManager
{
   int AddProperty(PropertyInfo pi);
   bool DeleteProperty(PropertyInfo pi);
   IList<PropertyInfo> GetAllProperties();
   bool UpdateProperty(PropertyInfo pi);
   int PropertyCount { get; }
}

The next question is, how do I develop the PropertyManager to allow the IPropertyDataManager classes to be injectable through code using the ProperyManager class? According to one of the seminal dependency injection resources, Martin Fowler's article, there are three different techniques to implement dependency injection: constructor injection, interface injection and setter injection. I will demonstrate each of these techniques.

Constructor Injection

The PropertyManager class has two dependencies, an IPropertyDataManager and an ILogger. With the constructor injection technique, I pass the specific IPropertyDataManager and ILogger implementation I want to use to the ProperyManager's constructor when I instantiate a PropertyManager object. Every subsequent call to PropertyManager's methods will use the specific implementation that was passed via the constructor.

Listing 2: Constructor Injection

public class PropertyManager
{
   //the IPropertyDataManager is a dependency of this class...
   private IPropertyDataManager dataManager;
   //the ILogger is another dependency of this class
   private ILogger logger;

   public PropertyManager(IPropertyDataManager dm, ILogger l)
   {
      //and we inject its implementation via the constructor
      this.dataManager = dm;
      this.logger      = l;
   }

   public int Add(PropertyInfo pi)
   {
      //todo: perform some validation
      int pID = this.dataManager.AddProperty(pi);
      this.logger.WriteLog("Added Property " + pID);
      return pID;
   }

   public bool Delete(PropertyInfo pi)
   {
      this.logger.WriteLog("Deleting Property " + pi.ID);
      this.dataManager.DeleteProperty(pi);
   }

   public IList GetPropertyList()
   {
      this.dataManager.GetAllProperties();
   }

   public bool Update(PropertyInfo pi)
   {
      bool didUpdate = this.dataManager.UpdateProperty(pi);
      return didUpdate;
   }
}

Now, I create a factory class that constructs and returns a PropertyManager object based on the context I am working in. If I am performing unit testing, I want a PropertyManager that does not interact directly with the database, and uses the ListPropertyDataManager. When I'm not unit testing, I would want a PropertyManager that uses the DbPropertyDataManager.

Listing 3: PropertyManagerFactory

public class PropertyManagerFactory
{
   public PropertyManager BuildTestPM()
   {
      ILogger logger = new SimpleLogger("simpleLogger");
      IPropertyDataManager pdm = new ListDataProperyManager();
      return new PropertyManager(pdm, logger);
   }

   public PropertyManager BuildDbPM()
   {
      ILogger logger = new SimpleLogger("simpleLogger");
      IPropertyDataManager pdm = new DbDataProperyManager();
      return new PropertyManager(pdm, logger);
   }
}

Now, when I'm writing code to use the PropertyManager class, I use the factory methods above to get the specific instance that I want.

Listing 4: Calling the PropertyManagerFactory

//create the instance to use for unit testing the PropertyManager
//class
PropertyManager unitTestInstance =
   PropertyManagerFactory.BuildTestPM();

//create the instance to use when I want actual DB interaction
PropertyManager dbInstance = PropertyManagerFactory.BuildDbPM();




Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel