依赖注入与单元测试用例






4.67/5 (5投票s)
通过一个简单的实际示例解释了依赖项的目的和实现。
引言
如今,依赖注入是面向对象编程中一个非常常用的术语。DI 是一种用于实现松耦合组件的设计模式。主要目的是代码可维护性和可重用性。您可能也听说过依赖注入容器,如 Unity、Ninject 或 Spring,但依赖注入是一个简单的概念,可以在任何地方使用,无需任何依赖注入容器,并且在单元测试中特别有用。
目标
在本文中,我们将涵盖
- 依赖注入的实现和优点
- 它如何简化编写测试用例
为了解释 DI,让我们以 Plane
为例。 Plane
可以有多个类,即 Business
、Premium
或 Economy
。现在假设所有这些类都实现一个接口 ItravelClass
。
public interface ITravelClass {
}
public class Business : ITravelClass {
}
public class Economy: ITravelClass {
}
public class Premium : ITravelClass {
}
现在要实现一个带商务舱的飞机,我们可以这样写
public class Plane {
private Business businessClass;
}
这是一架不错的 plane
,这个 plane
类与 Business
类紧密耦合。这没什么问题,Business
飞机很好,但如果我们决定允许 economy
类呢。然后我们必须修改这个类并添加 Economy
类的引用,这显然不是一个好方法。
使用接口
由于 Business
类实现了 ItravelClass
接口。其他类也实现了相同的接口。让我们思考一下。当我们设计 Plane
类时,我们可以在这里实例化 IteravelClass
的对象。让我们这样做
public class Plane {
private ITravelClass travelclass ;
}
但这只是一个接口,缺少一个具体类,所以我们必须实例化商务类的对象,如下所示
public class Plane {
private ITravelClass travelclass = new Business();
}
现在,这里我们使用接口,但它仍然有业务对象,仍然是紧密耦合。我们的问题仍然没有解决。
依赖注入
顾名思义,它都是关于注入依赖项并使类松耦合。这意味着我们必须使 Plane
类独立于具体类,并让它在运行时决定具体类的实现。
基于构造函数的注入
注入依赖项的一种方法是将依赖类的具体实现传递给构造函数。我们的 plane
类将变为
public class Plane {
private ITravelClass travelclass;
public Plane(ITravelClass travelclass){
this.travelclass = travelclass;
}
}
现在,我们可以通过从客户端传递具体类来创建带有任何类的 plane
。例如
class Program{
//constructor based DI client
static void Main(string[] args){
//constructor based DI client
Plane myBusinessPlane = new Plane(new Business());
myBusinessPlane.setClass(new Business());
Plane myEconomyPlane = new Plane(new Economy());
myEconomyPlane.setClass(new Business());
Console.ReadLine();
}
}
基于属性的注入
我们还可以通过在 plane
类中使用 setter 来注入依赖项,如下所示
public class Plane{
private ITravelClass travelclass;
public ITravelClass planeclass {
get {return travelclass ;}
set { travelclass = value;}
}
}
客户端的实现将相应更改
class Program
{
//DI using setter
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();
}
单元测试:依赖注入的用例
到目前为止,我们已经看到了如何实现依赖注入。现在,这样做有什么好处。一个是我们已经从 Plane
类中删除了紧密耦合。现在,依赖注入的一个实际的、经常使用的用例是在编写单元测试用例时。在上面的例子中,我们使用了没有任何复杂实现的简单类。让我们更改 ITravelClass
并添加两个方法 SetClass
和 getServices
,并在其他类中实现这些方法。
void setClass(String classtype, String priceRange);
String getServices();
假设在实际示例中,这些方法可能涉及一些复杂的数据库操作,我们不希望在我们的测试用例中使用这些操作。所以我们可以创建一个模拟类来实现 ITravelClass
并在方法中拥有我们自己的测试数据。
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;
}
}
单元测试类的实现如下所示
[TestClass]
public class ProgramTest
{
[TestMethod]
public void TestPlane(){
Plane myMockPlane = new Plane();
myMockPlane.planeclass = new MockClass();
Assert.AreEqual("MockClass", myMockPlane.planeclass.getServices());
}
}
所以现在我们已经实现了依赖注入,并成功地将其用于单元测试用例。总而言之,我们涵盖的一些重要点如下
- 使用接口而不是具体类来避免类中依赖项的紧密耦合。
- 依赖注入可以通过多种方式完成,例如构造函数注入或属性 setter 注入。
- 依赖注入使单元测试非常灵活。
对于完整的实现,请下载附加的代码。