Introduction
These days, Dependency Injection is a very commonly used term in object oriented programing. DI is a design pattern which is used to implement loosely coupled components. The main purpose is code maintainability and reusability. You may also have heard of the Dependency Injection container like Unity, Ninject or Spring but Dependency Injection is a simple concept which can be used anywhere, without any Dependency Injection container, and is especially useful in unit testing.
Objective
In this article, we will cover:
- Dependency Injection implementation and advantages
- How it can ease writing test cases
To explain DI, let us take an example of Plane
. Plane
can have multiple classes, i.e., Business
, Premium
or Economy
. Now let's say all these classes implement an Interface ItravelClass
.
public interface ITravelClass {
}
public class Business : ITravelClass {
}
public class Economy: ITravelClass {
}
public class Premium : ITravelClass {
}
Now to implement a plane with business class, we can write:
public class Plane {
private Business businessClass;
}
This is a nice plane
this plane
class is tightly coupled to the Business
class. There's nothing wrong with it, the Business
plane is fine but what if we decide to allow economy
class. Then we have to modify this class and add reference of Economy
class, which is obviously not a good approach.
Using Interface
As Business
class implement was implementing the ItravelClass
interface. Other classes are also implementing the same interface. Let's think about it. When we design our Plane
class, we can instantiate object of IteravelClass
here. Let’s do that:
public class Plane {
private ITravelClass travelclass ;
}
But this is just an interface not missing a concrete class so we have to instantiate business class object as shown below:
public class Plane {
private ITravelClass travelclass = new Business();
}
Now, here we are using interface but it still has business object still so tightly coupled. Our problem is still unresolved.
Dependency Injection
As the name says, it is all about injecting dependency and making class loosely coupled. That means we have to make Plane
class independent of concrete class and let it decide the concrete class implementation at run time.
Constructor Based Injection
One way to inject dependency is to pass the concrete implementation of the depending class to the constructor. Our plane
class would become:
public class Plane {
private ITravelClass travelclass;
public Plane(ITravelClass travelclass){
this.travelclass = travelclass;
}
}
Now, we can create plane
with any class by passing the concrete class from the client. For example:
class Program{
static void Main(string[] args){
Plane myBusinessPlane = new Plane(new Business());
myBusinessPlane.setClass(new Business());
Plane myEconomyPlane = new Plane(new Economy());
myEconomyPlane.setClass(new Business());
Console.ReadLine();
}
}
Property Based Injection
We can also inject dependency by using setter in the plane
class as shown below:
public class Plane{
private ITravelClass travelclass;
public ITravelClass planeclass {
get {return travelclass ;}
set { travelclass = value;}
}
}
Implementation of client will change accordingly:
class Program
{
static void Main(string[] args){
Plane myBusinessPlane = new Plane();
myBusinessPlane.planeclass = new Business();
Console.WriteLine(myBusinessPlane.planeclass.getServices());
Console.WriteLine("---------------------------------");
Plane myEconomyPlane = new Plane();
myEconomyPlane.planeclass = new Economy();
Console.WriteLine(myEconomyPlane.planeclass.getServices());
Console.ReadLine();
}
Unit Test: Use Case for Dependency Injection
Till now, we have seen how to implement dependency injection. Now what are the advantages of doing this. One was we have removed tight coupling from Plane
class. Now one of the real time frequently used use case of Dependency injection is in writing unit test cases. In the above example, we have used simple classes with any complex implementation. Let’s change ITravelClass
and add two methods SetClass
and getServices
and implement these in other classes.
void setClass(String classtype, String priceRange);
String getServices();
Suppose in real example these methods could involve some complex DB operations which we don’t want to use in our test case. So we can create a mock class which implements ITravelClass
and have our own test data inside methods.
public class MockClass :ITravelClass
{
private String classtype = "MockClass";
private String priceRange = "TEST data";
public void setClass(String classtype, String priceRange)
{
this.classtype = classtype;
this.priceRange = priceRange;
}
public String getServices()
{
return classtype;
}
}
Implementation of Unit test class is as follows:
[TestClass]
public class ProgramTest
{
[TestMethod]
public void TestPlane(){
Plane myMockPlane = new Plane();
myMockPlane.planeclass = new MockClass();
Assert.AreEqual("MockClass", myMockPlane.planeclass.getServices());
}
}
So now we have implemented a dependency injection and successfully used it in Unit test case. To sum up, some important points we covered are listed below:
- Use interface rather than concrete class to avoid tight coupling of dependency in the class.
- Dependency injection can be done in various ways like constructor injection or property setter injection.
- Dependency injection makes unit testing very flexible.
For complete implementation, download the attached code.